MCP App Store
by KeenEthics
Raylight icon

Raylight

By Frames of Motion Inc.

Get New App Alerts

Overview

Raylight lets ChatGPT make and edit motion design videos for you: product videos, launch videos, and app demos, built in Raylight's browser editor with real 3D camera moves, device frames, lighting, typography, and sound. ChatGPT works inside your open Raylight project the way a motion designer would. ChatGPT reads your project (shots, layers, colors, fonts, timing), makes edits in batches, then renders real frames of the result to check its own work and fix what reads wrong. Every change appears on your canvas as it happens and is undoable like your own edits. Hand it screenshots, logos, video clips, or audio and it places them; ask for a template and it starts from one of Raylight's official motion templates and swaps in your copy and media. What people use it for: turning a few screenshots into a launch video, animating a headline and a push-in, scoring cuts with library sound, making an existing video match a brand, reviewing a shot and tightening its timing. How it works: open your project at raylight.app, ask ChatGPT for a change, watch it happen. Edits and renders run through your open editor tab, so the tab stays open while ChatGPT works (it does not need to be visible). Exports and share links come from Raylight itself. Requires a Raylight account, which you can create from the sign-in screen when you connect. Every tool and example prompts: raylight.app/mcp/docs.

Tools

apply_edits

ChatGPT
Apply a batch of edits to the project. Each edit is an object with an "op" field plus op-specific fields. Every edit is validated ON ITS OWN and applied through the editor's own reconcilers, in order: a malformed edit fails as its own result ("Invalid <op>: ...") while the rest of the batch still runs, and results come back per edit so you can correct failures. Batch related edits into ONE call (up to 200 edits). Refs: any creating op (add_text, add_shape, add_image, add_icon, add_frame, frame_assets, add_shot, split_shot, duplicate_asset, copy_asset_to_shot, group_assets, add_animation, set_keyframes, add_group_animation, add_effect_animation, add_light, add_light_animation, add_camera_move, add_speed_cut, add_sound, create_component, insert_component) accepts ref: "name" (letters, digits, _ and -, not starting with a digit); once it succeeds, later edits IN THE SAME BATCH may write "$name" as the value of any string field (assetId, groupId, framerBlockId, shotId, targetAssetId, assetIds entries...) and it is replaced by the created id, so one call can add a layer and animate it. An unknown $name fails only that edit and lists the refs bound so far. Every successful creating result carries a structured created object ({assetId?, shotId?, animationId?, animationIds? (set_keyframes: every fitted block), framerBlockId?, groupId?, lightId?, componentId?, soundAssetId?}) plus ref when one was given; the top level carries refs (name -> id) and createdIds (every id the batch created). The note still spells the ids out as text. Batch flags next to edits: lint: false skips the pre/post composition lint (faster, no lint in the result); bypassTasteRules: true skips the taste-only guards (the AI-poster text-stack rejection, the camera-wobble rejection, the text size ceilings) for callers who know what they want; hard limits (lane overlap, schema ranges, shot bounds) always apply. When the batch creates or restyles text, the result also carries measuredTextWorld (assetId -> measured world footprint, same shape as sizeWorld) once the text renders; use it for layout math instead of re-reading the shot. The result may also carry lint: deterministic geometry findings your batch INTRODUCED in the shots it touched (layers clipped by the frame edge, buried behind an opaque layer in paint order, colliding text, frame members clipped away, near-miss alignment, cramped gaps), each with severity, the asset ids involved, and how to fix it. Findings are diffed against the shot's pre-edit state, so treat every lint warning as a mistake you just made and correct it in your next apply_edits call; preExistingLintCount reports how many older findings the shot already had (read the shot to see them, and fix them only when they relate to your task). Ops and their fields (all times in ms; asset/animation/camera times are SHOT-LOCAL; angles in DEGREES; positions in absolute world units where the canvas center is the overview's canvasWorldUnits.center (NOT necessarily (0,0)) and +y is UP; keep x/y inside the overview's visibleX/visibleY or the layer is off-screen): - update_asset {shotId, assetId, position?: {x?,y?,z?} (absolute world; canvas center = canvasWorldUnits.center, +y up), rotationDeg?: {x?,y?,z?} (z = in-plane roll, x/y = 3D tilt), scale?: number (absolute RESTING scale; media layers rest at their placement fit factor, not 1: read the current scale from get_shot and multiply for relative resizes. Existing scale animation blocks follow the change proportionally. REJECTED on text layers: text size is font.size; use set_text_font), opacity?: 0..1, hidden?: bool, name?: string, flipX?: bool, flipY?: bool (render-only mirror; footprint and position unchanged), pivot?: center|top-left|top|top-right|left|right|bottom-left|bottom|bottom-right | {x: -1..1, y: -1..1} | null (the transform origin for MOTION: rotation/tilt/scale blocks, entrance/exit scale and shape/frame size blocks swing about this point of the layer's box, and the point itself holds still. It never moves the layer at rest; position stays the box center and resting rotationDeg/scale sti…

check_sync

ChatGPT
MEASURE audio-visual sync: detects transient onsets in the real project mix and correlates them with the span's visual beats (cuts, entrances, animation starts). Returns JSON: per beat, the nearest onset and the signed offset in ms (positive = the sound lands LATE, negative = early, null = no sound marks that moment within 250ms), plus unmatched onsets (sounds that mark nothing visual). Use it after sound work to verify hits land where they should, then fix offsets with move_sound (impacts land ON the cut; whooshes typically lead it by ~60-120ms so the energy peak arrives with the visual). Omit startMs/endMs to check the whole timeline. A null onset on a beat you scored means the sound is missing, too quiet, or too soft-attacked to register - render_waveform shows which.

discard_import_session

ChatGPT
Throw away a pending After Effects import the user started and will not finish (ids from get_import_status pendingSessions). Removes the stored capture and that import's uploads. The project is deleted only when that import created it and nothing was ever imported or built in it; a project that existed before, or already holds a finished import, is never deleted. Cannot be undone, so ask the user first. A finished import cannot be discarded.

file_shared_project

ChatGPT
Choose which of the user's workspaces a project someone shared with them shows up in on their projects page. Only for sharedWithMe entries from list_projects. This is the user's own filing: it gives nobody access and the owner's project is not moved.

get_design_guide

ChatGPT
Raylight's motion design guide, distilled from the official hand-built templates (recreations of top-tier motion design from Linear, Apple, Fey, Revolut and others). Topic "craft-rules" returns the full craft ruleset (identical to the in-app director's brief; skip it if those rules are already in your system prompt). Topic "measured-craft" returns the aggregate timing/text/camera/effects numbers; a template topic returns that template's scene-by-scene beat sheet (exact arrival times, amounts, easings, camera beats, palette) plus preview images. The templates are the ground truth of what looks good: fetch the closest template's topic BEFORE authoring any scene's motion (not just when recreating a known look) and mirror or transplant its beats; its "Scene N" numbering matches apply_template_motion's sceneIndex. Topics: craft-rules, measured-craft, 3d-mobile-app, twitter-app, raylight-launch, airbnb-3d-app, nowak, liquid-glass, linear-agent, revolut-app, framer, raylight-agent-launch, raylight-marketplace, macbook-pro-around-linear, linear-hero, image-slide, manicule, limelight, drop-in, fey, lines, linear-reviews, prompt-button-press, bouncy-liquid-glass, apple-news, rubik-s-cube-liquid-glass.

get_editor_capabilities

ChatGPT
Return the full list of tools this server exposes and the recommended workflow. Optional discovery helper: you never need to call this before working, just start with get_editor_status. It exists so a capabilities query resolves cleanly instead of erroring.

get_editor_status

ChatGPT
Check whether the user has a Raylight editor tab open and connected, and which project it is editing. Call this FIRST: every editing and rendering tool routes through that live tab. If nothing is connected, tell the user to open their project at raylight.app (use list_projects to give them the exact link) and wait for them to confirm.

get_import_report

ChatGPT
Read the import report of a project that was brought in from After Effects, as it stands NOW: per source layer whether it became editable Raylight layers (native), was rendered to an image or video (bake-image / bake-video), was dropped or skipped, with reason codes and messages; the rendered media (bakes); warnings; the reference frames; the fidelity score when one was measured; and stats recomputed from the layers the project still has (stats.removed = imported layers the user has since deleted, which are left out of layers). Use it before rebuilding a rendered layer natively, and after, to confirm the report changed. Omit projectId to read the open project (in the app that includes unsaved edits); pass projectId (from list_projects) to read another project. Errors when the project was not imported, and the error says how the user starts an import.

get_import_status

ChatGPT
Check where the user's After Effects imports stand. Returns pendingSessions (imports started in the After Effects panel that have not finished: sessionId, projectId, projectName, the comp's name and size, createdAt, expiresAt, status pending or expired), project (when projectId is passed: whether it was imported and its live report), and howToImport (the exact steps the user follows in After Effects). You cannot start an import yourself: it needs After Effects open on the user's machine. When the user asks to import or re-import a comp, give them the howToImport steps, then call this again to see the session appear and get_import_report once it lands. Works without an open editor tab.

get_project_overview

ChatGPT
Read the whole project at a glance: the coordinate system and visible canvas bounds in world units (canvasWorldUnits.center + visibleX/visibleY; the center is NOT always (0,0)), every shot with its absolute start/duration and asset list, the styleDigest (every color in use with its roles, every font family with its weights: match these when adding elements so the project stays cohesive), the mediaLibrary (every image/video/audio available to the project: media used in shots plus everything uploaded to the Assets panel, usageCount 0 = uploaded but not yet placed; placeable via add_image/add_shot), where the user currently is (playhead, current shot, selection), and on projects imported from another app an import block (decision counts, fidelity score, and the renderedLayers that were placed as media because they could not be rebuilt natively, each with its assetId and reason code). A fresh copy of this exact data is already included in the user's message as project_state; call this tool only to RE-read after apply_edits changes the project, never at the start of a run.

get_shot_details

ChatGPT
Read one shot in full detail: every asset with transform (world units, degrees), MEASURED rendered footprint (sizeWorld: width/height plus left/right/top/bottom world edges; use these for spacing and overlap math, never guess sizes), lifetime (shot-local ms), entrance/exit, animation blocks, per-kind extras (crop and video trim, enabled layerEffects, frame size/clipContent, containerId = the frame a layer belongs to, icon identity on inserted icons, audio mix, material on extruded layers and bevel on extruded paths), groups with their members, their explicit pivot when one is set (absent = the measured visual center of the members), and group animation blocks, plus camera framings/moves (framings report rollDeg when set), enabled effects, and the key light for the shot (resting pose plus its animation blocks) when it has one. Fields at their rest defaults are OMITTED from asset summaries: no rotationDeg means {0,0,0}, no scale means 1, no opacity means 1, no entrance/exit means type "none", no name means unnamed, no depth on a shape, path, or text layer means flat (depth is reported in world units when the layer is extruded), no material means Matte, no bevel means square depth edges, no light means the shot is unlit, and only enabled effects are listed. Scale animation blocks report amount as amountUnit "multiplierOfRest"; the same multiplier basis add_animation accepts, so a block you wrote as 1.1 reads back as 1.1 and can be passed back to update_animation unchanged. Non-uniform scale reads back as a {x, y, z} object instead of a single number (read-only: update_asset.scale writes a uniform scalar, so adjust size via sizeWorld-aware position/size edits rather than by flattening a non-uniform scale). When the shot has geometric problems, the payload includes lint: findings for layers clipped by the frame edge, buried behind opaque layers in paint order, colliding text, frame members clipped away, near-miss alignment, and cramped gaps; fix warnings before adding anything new. Call this for any shot you intend to edit before editing it.

list_projects

ChatGPT
List the user's Raylight projects (newest first, capped at 50) with an editUrl for each, plus the projects other people shared with them by link or invite that aren't already in one of the user's workspaces (sharedWithMe, with sharedBy and which of the user's workspaces each is filed under) and the user's workspaces. Works even when no editor tab is connected, so use it to tell the user exactly which link to open.

list_templates

ChatGPT
List Raylight's ready-made templates (free ones only), best first. Each entry has a slug, name, category, styles, duration, and slot counts, plus a designGuideTopic when a get_design_guide style card exists for it. Entries WITH a designGuideTopic are official hand-built Raylight templates; prefer those. Use this to pick a starting point for start_from_template, which beats building from an empty canvas: the motion craft comes pre-built and you swap in the user's content.

list_videos

ChatGPT
List the user's exported videos (hosted share pages), newest first, capped at 50. Each entry has a shareId, title, shareUrl, status (ready or pending), visibility ("public" = anyone with the link can watch, "private" = only the user; a private video's shareUrl does not play for anyone else, so check it before handing out a link, and change it with update_share_page video.visibility, asking the user before making a video public), durationMs, viewCount, reactions (count per one-tap reaction viewers left), signups (new Raylight accounts that started on that video's page; each one counts toward the user's referral earnings), and the video's page settings (description, cta, allowRemix). Use it to find the shareId for update_share_page, or to hand the user a link to a video they already exported.

render_filmstrip

ChatGPT
SEE MOTION: renders 4, 6, or 9 evenly spaced real frames across a time span and returns them as ONE contact-sheet image (reading order left to right, top to bottom, each tile labeled with its absolute time). This is how you judge animation: whether an entrance cascade actually cascades, whether a camera move eases in, whether elements collide mid-flight, how a cut lands. Span one beat at a time: an entrance (shot start to start + ~1200ms), a camera move, the 600ms around a cut. Use 6 frames by default, 9 for a long or busy span, 4 for a quick check. Tiles are smaller than single stills; for fine detail or small-text legibility follow up with render_stills (optionally with a region).

render_stills

ChatGPT
SEE your work: renders 1-4 real frames of the final film at the given ABSOLUTE project times and returns them as images. Each frame is the true render the user will see: camera moves, entrance/exit animations mid-flight, animation blocks, effects, background, everything. Use this after apply_edits to review composition, spacing, legibility, and effect intensity with your own eyes, then fix what reads wrong and render once more to confirm. Pick decisive moments: just after entrances settle (shot start + ~700ms), the composed mid-shot hold, just before a cut. To judge motion over time, prefer render_filmstrip. Pass region to ZOOM onto part of the canvas at full resolution: use it to verify small text is legible and details are crisp (region frames render with camera moves suppressed, so also check the real framing without a region). The first content block is a JSON calibration record: with a region it is {regionView: {zoom, center, visibleWorld {minX,minY,maxX,maxY}, imagePx, pxPerWorldAtZ0}} computed from the framing actually rendered (the region is FIT inside the image at the project aspect, so visibleWorld is usually wider or taller than the rect you asked for; never assume pxPerWorld = imagePx.width / region.width), and without a region it is {frameView: {imagePx, canvasWorld, pxPerWorldAtRest}} (exact only while no camera move is active). Each still's label also carries layers: [{assetId, name, kind, px: {x, y, w, h}}], the resting pixel rect of every visible layer in that image (top-left origin, z=0 plane through the camera active at that time), so you can point at what you see. A frame with a missing or half-loaded asset means media was still loading; render that time again.

render_waveform

ChatGPT
HEAR your work: renders the real project mix across a time span as a waveform + spectrogram strip (one image, timeline-aligned), with the span's visual beats - cuts, entrances, animation starts - overlaid as numbered markers and listed with absolute times. This is the audio filmstrip: use it after placing sounds to see whether transients land on the markers, whether a music bed leaves room for SFX (spectrogram shows competing energy), whether a sound is present at all, and where dead air sits. The mix is the true export mix: block envelopes, fades, per-layer effects, ducking, everything. Span one moment at a time (the ~1500ms around a cut or entrance) for precise reading, or a whole shot for the energy arc. For exact numeric offsets between sounds and beats, use check_sync; this tool is for seeing the shape.

save_as_video

ChatGPT
Copy the CURRENT project, which must be a design (a still: get_project_overview reports a designSize), into a NEW video project so it can be animated. The layers, lights, effects, and background carry over; the canvas snaps to the nearest video aspect ratio and the camera pulls back if needed so the whole design stays in frame. Returns the new project id and an editUrl. It does NOT open or switch to the new project: give the user the editUrl and keep working in the current project unless they switch. Pass "name" to title it (default: "<design name> (Video)"). Use when the user wants motion on a design; a design itself never plays.

score_import_fidelity

ChatGPT
Measure how closely the open imported project matches After Effects: renders the project at each reference instant the importer saved, compares it with After Effects' own render of that instant, and stores the result on the project. Returns meanScore (0 to 1, structural similarity; 1 = identical) and frames [{ms, score, error?}], lowest first, so you can see WHEN the project drifts from the source and inspect those moments with render_stills. Run it after rebuilding a rendered layer to confirm the rebuild still matches. Needs the project open in the editor; errors when the project was not imported or has no reference frames. Read a previously stored score with get_import_report (fidelity).

start_from_template

ChatGPT
Create a NEW project for the user from a template (slug or id from list_templates; free templates only). Returns the project id and an editUrl. It does NOT open or switch to the new project: give the user the editUrl and keep working in the current project unless they switch. Pass "name" to title the project. Use when the user wants to start something new that a template fits; do not use to modify the current project.

update_share_page

ChatGPT
Customize the user's share page: the Linktree-style page every exported video lands on (their name and avatar, a headline, the video, link buttons, colors). "page" edits the page itself and applies to every video: headline (one line under the name, max 80 chars), links (up to 8 {label, url} buttons; the full list replaces the old one), background and accent (#rrggbb; text color follows the background automatically), backgroundGradient (a gradient over the background color, or null for a flat color: {gradientType: "linear" | "radial" | "angular" | "diamond", angle in degrees where 0 = bottom to top, 90 = left to right, 180 = top to bottom, stops: 2 to 12 of {position 0-1, color #rrggbb}}; when setting one, also set background to its first stop), pattern (a faint repeating texture on top: "none", "dots", "grid", "warp" (a wavy grid symmetric around the middle of the screen), "waves", "mesh", "rings", "ripple", "blobs", "dither", "grain"), buttonStyle ("fill", "outline", "soft"), showDownload, showShareLinks (the copy link, X and LinkedIn buttons under the video), hideBranding (removes the "Made in Raylight" credit; Max plan only. Free pages always show a "Made in Raylight" block with a sign-up button and an end card when the video finishes; Hobby and Pro pages show a small credit in the footer). Free plans can set headline and links only; the other fields answer with an error naming the plan gate, so tell the user instead of retrying. "video" edits one video and needs shareId (from list_videos): title, description (under the video, max 500 chars), cta ({label, url} button above the links, null to remove), allowRemix (lets viewers open a live copy; only shows on a public video made from a project the user owns, video.remixAvailable in the result says whether it is live; turning it off kills the remix link on the page right away), visibility ("public" = anyone with the link can watch, "private" = only the user; ask before making a video public), showViewCount ("everyone" shows the view count on the page, "owner" hides it from visitors). Returns the resulting page. Use when the user asks to brand, personalize, or add links or a call to action to their share link.

upload_media

ChatGPT
Put an image, video, or audio file into the project's asset library so apply_edits can place it (add_image {src}, add_shot {mediaSrc}, set_background {image}, add_sound {mediaSrc}). Use it whenever the user gives you a file or a link to one. Three ways to call it, chosen by which fields you pass: 1. {url}: a direct, publicly reachable link to the file itself (not a page that embeds it). The server fetches it (max 100 MB). This is the usual path. 2. {base64, mime}: the file bytes inline. Small files only (under 5 MB decoded; a data: URL is accepted). 3. {mime, bytes} with no url/base64: for a LOCAL file you can read with a shell but cannot inline. Returns an uploadUrl; PUT the raw bytes to it (curl -X PUT -H "Content-Type: <mime>" --upload-file <path> "<uploadUrl>"), then call upload_media again with {assetId} to finish. bytes must be the exact file size; for video over 100 MB also pass durationMs. Bulk (up to 20 per call, one result per item in input order, one bad item never fails the others): {urls: [{url, name?, mime?}]} fetches several links (way 1) and returns results[]; {files: [{mime, bytes, name?, durationMs?}]} reserves several signed PUT slots (way 3) and returns uploads[] each with assetId + uploadUrl + headers + name; {assetIds: [...]} finishes several signed uploads in one call (safe to retry: already-finished ids return ok). Each item carries ok plus either the asset fields or an error; retry only the failed items. Supported: image/png, image/jpeg, image/webp, image/gif, video/mp4, video/webm, video/quicktime, audio/mpeg, audio/wav, audio/mp4, audio/x-m4a, audio/aac. The file is measured (width/height, duration) and appears in the user's Assets panel immediately with an "Add to canvas" toast; the result's src, libraryRef, and name are all accepted by apply_edits. Videos are stored as given (no re-encode): prefer H.264 mp4.

Capabilities

Writes

Example Prompts

Click any prompt to copy it.

Similar Apps