back to tools

VisualSource v4

public REST API reference

API Reference

Public REST API for encoding, decoding, and compiling VisualSource scripts. All endpoints accept and return JSON unless noted otherwise.

Base URL https://retrostudio-visualsource.onrender.com
Endpoints
POST /encode

Encodes a decoded VisualSource script into the compact base93 format (deflate + base93). The output is ready to paste into RetroStudio.

Request body
FieldTypeRequiredDescription
textstringrequiredRaw decoded VS script text.
keep_originalbooleanoptionalWrap output with control chars. Defaults to true.
Example body
{ "text": "EditorCameraPosition0,0...", "keep_original": true }
Response
{ "output": "\x1a0000000000000004...", "stats": { "input_chars": 142, "compressed_bytes": 89, "output_chars": 124 } }
POST /decode

Decodes a base93-encoded VisualSource script string into human-readable block format. Supports legacy and current versions.

Request body
FieldTypeRequiredDescription
textstringrequiredEncoded VS script string.
keep_originalbooleanoptionalPreserve control-char separators. Defaults to true.
Example body
{ "text": "\x1a0000000000000004...", "keep_original": true }
Response
{ "output": "EditorCameraPosition0,0...", "stats": { "version": "4", "output_chars": 142 } }
POST /toluau

Compiles a decoded VS script into Luau source code and returns it directly.

Request body
FieldTypeRequiredDescription
textstringrequiredDecoded VS script text.
parallelize_thresholdintegeroptionalLoop blocks with more direct children than this run the extra children in task.spawn. Defaults to 0.
wrap_in_pcallbooleanoptionalWrap parallelized loop children and function bodies in pcall, logging errors via warn(). Defaults to false.
strict_modebooleanoptionalAdds --!strict to the top of the generated Luau. Defaults to false.
minifybooleanoptionalStrip comments and compact indentation/blank lines. Defaults to false.
use_task_delay_for_waitbooleanoptionalWait blocks with children emit task.delay instead of task.wait followed by the children inline. Ignored if legacy_wait is on. Defaults to false.
indent_stylestringoptionalOne of tab (default), space2, space4.
add_block_commentsbooleanoptionalPrefix each emitted block with -- BlockName (BlockType). Defaults to false.
safe_navigationbooleanoptionalMulti-segment Object paths resolve intermediate hops with :FindFirstChild(...) instead of direct indexing. Defaults to false.
emit_type_annotationsbooleanoptionalAnnotate types on new local declarations from SetVariable blocks when the type is known. Defaults to false.
legacy_waitbooleanoptionalEmit wait(n) instead of task.wait(n). Takes priority over use_task_delay_for_wait. Defaults to false.
Example body
{ "text": "EditorCameraPosition0,0...", "parallelize_threshold": 0, "wrap_in_pcall": false, "strict_mode": false, "minify": false, "use_task_delay_for_wait": false, "indent_style": "tab", "add_block_comments": false, "safe_navigation": false, "emit_type_annotations": false, "legacy_wait": false }
Response
{ "output": "local part = Instance.new(\"Part\")..." }
POST /luautos

Compiles Luau source code into an encoded VS script.

Request body
FieldTypeRequiredDescription
textstringrequiredLuau source code.
service_remapobjectoptionalMap of service name overrides, e.g. {"Players": "MyPlayers"}. Defaults to {}.
remap_textsize_to_fontsizebooleanoptionalRewrite TextSize property access to FontSize. Defaults to true.
Example body
{ "text": "local part = Instance.new(\"Part\")", "service_remap": {}, "remap_textsize_to_fontsize": true }
Response
{ "output": "\x1a0000000000000004...", "stats": { "blocks": 12, "decoded_chars": 1840, "compressed_bytes": 712, "encoded_chars": 950 }, "compat": { "score": 100, "issues": [] } }
POST /luautos2

Compiles Luau source into an encoded VS script using the newer AST-based compiler. Covers locals/assignments, function calls, if/elseif/else, while, and numeric for loops.

Request body
FieldTypeRequiredDescription
textstringrequiredLuau source code.
service_remapobjectoptionalMap of service name overrides, e.g. {"Players": "MyPlayers"}. Defaults to {}.
remap_textsize_to_fontsizebooleanoptionalRewrite TextSize property access to FontSize. Defaults to true.
Example body
{ "text": "local part = Instance.new(\"Part\")", "service_remap": {}, "remap_textsize_to_fontsize": true }
Response
{ "output": "\x1a0000000000000004...", "stats": { "blocks": 8, "decoded_chars": 1120, "compressed_bytes": 480, "encoded_chars": 620 }, "compat": { "score": 100, "issues": [] } }
POST /luautos3

Compiles Luau source using the original almostIDE plugin engine, run standalone outside Roblox Studio.

Request body
FieldTypeRequiredDescription
textstringrequiredLuau source code.
Example body
{ "text": "local part = Instance.new(\"Part\")" }
Response
{ "output": "\x1a0000000000000004...", "stats": { "blocks": 8, "encoded_chars": 950 }, "compat": { "score": 100, "issues": [] } }
POST /compatcheck

Checks compatibility by actually compiling the input with the selected engine, then analysing what the engine produced or rejected. Each engine reports different things because each engine supports a different subset of Luau. Returns a score from 0–100 and a list of issues. Always returns HTTP 200 — a score of 0 with a compat.error field means the input was invalid or failed to compile, not that the server failed.

Request body
FieldTypeRequiredDescription
compilerstringrequired"TO-VS" to check Luau→VS compatibility. "TO-LUAU" to check VS→Luau compatibility.
textstringrequiredLuau source when compiler is "TO-VS". Encoded VS, decoded-binary VS, or a human-readable block dump when compiler is "TO-LUAU".
arg1stringoptionalOnly used when compiler is "TO-VS". Selects the engine to test against: "v1", "v2" (default), or "almostlua". Each engine compiles the input for real and the result reflects what that specific engine actually supports.
Each engine uses a different analysis strategy. almostlua uses its own internal compatibility tracker built into the engine. v1 and v2 compile the source and scan the generated block tree for [Unsupported] and [unsupported for-in] markers — constructs the compiler could not map to a VS block are emitted as annotated comment blocks instead of being silently dropped, so the scan is exhaustive. A hard parse or codegen failure in v1/v2 sets score to 0 and fills compat.error.
Example body (TO-VS)
{ "compiler": "TO-VS", "text": "local part = Instance.new(\"Part\")\npart.Parent = workspace", "arg1": "v2" }
Example body (TO-LUAU)
{ "compiler": "TO-LUAU", "text": "\x1a0000000000000004..." }
Response
{ "compiler": "TO-VS", "target": "v2", "compat": { "score": 85, "issues": [ { "category": "METHOD", "name": "HttpService:GetAsync", "count": 1 } ] } }
When the input fails syntax validation or the engine cannot compile it, compat.score is 0 and compat.error contains the reason. The response is still HTTP 200.
POST /pm-encode

Compresses a JSON value into the PotatoMod format (deflate + base93, prefixed with PotatoModHeader_). Mirrors the compressTable function from the PotatoMod v2.3 plugin.

Request body
FieldTypeRequiredDescription
textstringrequiredA valid JSON string (object or array) to compress.
Example body
{ "text": "{\"key\": \"value\"}" }
Response
{ "output": "PotatoModHeader_<base93 data>", "stats": { "input_chars": 142, "compressed_bytes": 89, "output_chars": 124 } }
POST /pm-decode

Decompresses a PotatoMod-encoded string back into pretty-printed JSON. Roblox types (Color3, Vector3, CFrame, etc.) are deserialized into readable constructor strings (e.g. Color3.fromRGB(...)).

Request body
FieldTypeRequiredDescription
textstringrequiredString starting with PotatoModHeader_.
Example body
{ "text": "PotatoModHeader_<base93 data>" }
Response
{ "output": "{\n \"key\": \"value\"\n}", "stats": { "version": "potatomoddata", "output_chars": 142 } }
POST /rc-encode

Compresses plain text using the RetroCompress LZW algorithm (port of RetroCompress.lua from the almostIDE / SaveBlockArray plugin). Output is a custom base93 stream, not deflate-based.

Request body
FieldTypeRequiredDescription
textstringrequiredAny plain text to compress.
Example body
{ "text": "hello world" }
Response
{ "output": "!12,11|<code sequence>", "stats": { "input_chars": 142, "compressed_bytes": 96, "output_chars": 96 } }
POST /rc-decode

Decompresses a RetroCompress string back into plain text.

Request body
FieldTypeRequiredDescription
textstringrequiredString starting with ! (RetroCompress format).
Example body
{ "text": "!12,11|<code sequence>" }
Response
{ "output": "the original decompressed text", "stats": { "version": "retrocompress", "output_chars": 142 } }
POST /VS-TO-JSON2

Converts a decoded VS script string into a structured JSON representation of the block tree.

Request body
FieldTypeRequiredDescription
textstringrequiredDecoded VS script text.
Example body
{ "text": "EditorCameraPosition0,0..." }
Response
{ "Editor": { "CameraPosition": "0,0", "CameraZoom": "1" }, "Blocks": [ { "BlockType": "Print", "Name": "Print1", "Inputs": { "Text": { "value_type": "0", "value": "hello", "data_type": "String" } }, "Outputs": {}, "ChildBlocks": [], "ElseChildBlock": null, "VisualPosition": "0,0" } ] }

On failure, returns {"ok": false, "error": "..."} instead.

POST /JSON-TO-VS2

Converts a block tree JSON (as produced by /VS-TO-JSON2) back into an encoded VS script string.

Request body
FieldTypeRequiredDescription
EditorobjectoptionalEditor metadata (CameraPosition, CameraZoom). Defaults to 0,0 / 1.
BlocksarrayrequiredArray of block objects matching the /VS-TO-JSON2 output schema.
Example body
{ "Editor": { "CameraPosition": "0,0", "CameraZoom": "1" }, "Blocks": [ { "BlockType": "Print", "Name": "Print1", "Inputs": { "Text": { "value_type": "0", "value": "hello", "data_type": "String" } }, "Outputs": {}, "ChildBlocks": [], "ElseChildBlock": null, "VisualPosition": "0,0" } ] }
Response
{ "ok": true, "output": "\x1a0000000000000004...", "stats": { "blocks": 1, "comments": 0, "decoded_chars": 84, "compressed_bytes": 60, "encoded_chars": 78 } }
POST /VS-TO-JSON

Converts an encoded or decoded VisualSource string into a JSON structure with lowercase field names. Used internally by /encode. For the capitalized BlockType/Inputs schema, use /VS-TO-JSON2 instead.

Request body
FieldTypeRequiredDescription
textstringrequiredEncoded or decoded VS script text.
Example body
{ "text": "EditorCameraPosition0,0..." }
Response
{ "ok": true, "editor": { "camera_x": 0, "camera_y": 0, "camera_zoom": 1 }, "blocks": [ { "type": "Print", "name": "Print1", "visual_pos": "0,0", "child_blocks": [], "else_child": "", "inputs": [ { "name": "Text", "value_type": "0", "value": "hello", "data_type": "String" } ], "outputs": {} } ], "comments": [] }
POST /JSON-TO-VS

Converts a JSON structure (as produced by /VS-TO-JSON) back into an encoded VisualSource string. Used internally by /encode. For the capitalized BlockType/Inputs schema, use /JSON-TO-VS2 instead.

Request body
FieldTypeRequiredDescription
blocksarrayrequiredArray of block objects matching the /VS-TO-JSON output schema.
editorobjectoptionalEditor metadata (camera_x, camera_y, camera_zoom). Defaults to {}.
commentsarrayoptionalArray of comment objects to embed in the script. Defaults to [].
Example body
{ "editor": { "camera_x": 0, "camera_y": 0, "camera_zoom": 1 }, "blocks": [ { "type": "Print", "name": "Print1", "visual_pos": "0,0", "child_blocks": [], "else_child": "", "inputs": [ { "name": "Text", "value_type": "0", "value": "hello", "data_type": "String" } ], "outputs": {} } ], "comments": [] }
Response
{ "ok": true, "output": "\x1a0000000000000004...", "stats": { "blocks": 1, "comments": 0, "decoded_chars": 84, "compressed_bytes": 60, "encoded_chars": 78 } }
POST /image-to-vs

Converts an uploaded image into a VS script that recreates it as a grid of parts. Accepts multipart/form-data, not JSON.

Form fields
FieldTypeRequiredDescription
imagefilerequiredImage file to convert.
sizeintegeroptionalTarget grid resolution. Defaults to 100, clamped between 1 and 256.
Response
{ "output": "\x1a0000000000000004...", "stats": { "size": 100, "blocks": 10000, "decoded_chars": 84200, "compressed_bytes": 6100, "encoded_chars": 7300 } }
POST /json-to-rbxmx

Converts a JSON tree of Roblox instances into a valid .rbxmx file (XML format version 4). Script instances with a VisualSource property are automatically compiled to Luau. Returns raw XML, not JSON.

Request body
FieldTypeRequiredDescription
Either an array of instance objects, or a dict of {id: instance}. Each instance carries ClassName, Name, Parent (or ParentID), and its properties.
Example body
[ { "ClassName": "Part", "Name": "Baseplate", "Parent": "Workspace" }, { "ClassName": "Script", "Name": "Script1", "Parent": "Workspace.Baseplate", "VisualSource": "EditorCameraPosition0,0..." } ]
Query parameters
ParameterTypeRequiredDescription
rename_velocitybooleanoptionalConvert legacy Velocity/RotVelocity to AssemblyLinearVelocity/AssemblyAngularVelocity. Defaults to false.
emit_unique_idbooleanoptionalInclude a synthetic UniqueId on each instance. Defaults to true.
parallelize_thresholdintegeroptionalLoop blocks with more direct children than this run the extra children in task.spawn. Defaults to 0.
wrap_in_pcallbooleanoptionalWrap parallelized loop children and function bodies in pcall, logging errors via warn(). Defaults to false.
strict_modebooleanoptionalAdds --!strict to the top of the generated Luau. Defaults to false.
minifybooleanoptionalStrip comments and compact whitespace in generated Luau. Defaults to false.
use_task_delay_for_waitbooleanoptionalWait blocks with children emit task.delay instead of task.wait followed by the children inline. Ignored if legacy_wait is on. Defaults to false.
indent_stylestringoptionalOne of tab (default), space2, space4.
add_block_commentsbooleanoptionalPrefix each emitted block with -- BlockName (BlockType). Defaults to false.
safe_navigationbooleanoptionalMulti-segment Object paths resolve intermediate hops with :FindFirstChild(...) instead of direct indexing. Defaults to false.
emit_type_annotationsbooleanoptionalAnnotate types on new local declarations from SetVariable blocks when the type is known. Defaults to false.
legacy_waitbooleanoptionalEmit wait(n) instead of task.wait(n). Takes priority over use_task_delay_for_wait. Defaults to false.
These codegen options only affect Script/LocalScript/ModuleScript instances that carry a VisualSource property, which get compiled to Luau automatically. Non-fatal warnings (e.g. ambiguous hierarchy) are returned in the X-Rbxmx-Warnings response header, separated by |.
Response
<roblox xmlns:xmime="..." version="4"> ... </roblox>
POST /parse

Parses a decoded VS script and returns a preview-oriented block structure used by the VS Preview panel. Also accepts GET with ?text= as a query parameter.

Request body
FieldTypeRequiredDescription
textstringrequiredDecoded VS script text.
Example body
{ "text": "EditorCameraPosition0,0..." }
Response
{ "ok": true, "editor": { "camera_x": 0, "camera_y": 0, "camera_zoom": 1 }, "blocks": [ { "type": "Print", "name": "Print1", "x": 0, "y": 0, "has_pos": true, "visual_position": "0,0", "child_blocks": [], "else_child": "", "inputs": [ { "name": "Text", "value_type": "0", "value": "hello", "data_type": "String" } ], "outputs": {} } ] }
GET /findblock?name=:name

Looks up a block by type name and returns its metadata from the block list. Also accepts POST with {"name": "..."} as the body.

Query parameters
ParameterTypeRequiredDescription
namestringrequiredBlock type name (e.g. Print, PlayerAdded).
Response
{ "found": true, "block_type": "Print", "category": "Output", "visual_name": "Print", "description": "Prints a value to the output.", "context": "...", "flags": "...", "inputs": "...", "outputs": "...", "raw": "## Block: Print\n..." }