Create motion and play it on The Handy, Autoblow, VacuGlide, Kiiroo, OSSM and Lovense toys.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Delete points. Give axis to clear one axis, or leave it out for every axis. Give from_ms and to_ms to clear only that range. Only when the user asks to clear, delete or start over.
End of the time range in milliseconds.
Start of the time range in milliseconds on the video timeline.
The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address.
The server marks this tool as destructive. Running it may delete or overwrite data on the server or in any account you connected with your headers.
Delete points. Give axis to clear one axis, or leave it out for every axis. Give from_ms and to_ms to clear only that range. Only when the user asks to clear, delete or start over.
| Argument | Type | Description |
|---|---|---|
| axis | any | |
| to_ms | any | End of the time range in milliseconds. |
| from_ms | any | Start of the time range in milliseconds on the video timeline. |
| editor_code | any | The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address. |
{
"type": "object",
"title": "clear_pointsArguments",
"properties": {
"axis": {
"anyOf": [
{
"enum": [
"stroke",
"surge",
"sway",
"twist",
"roll",
"pitch",
"vibrate"
],
"type": "string"
},
{
"type": "null"
}
],
"title": "Axis",
"default": null
},
"to_ms": {
"anyOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "null"
}
],
"title": "To Ms",
"default": null,
"description": "End of the time range in milliseconds."
},
"from_ms": {
"anyOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "null"
}
],
"title": "From Ms",
"default": null,
"description": "Start of the time range in milliseconds on the video timeline."
},
"editor_code": {
"anyOf": [
{
"type": "string",
"maxLength": 16
},
{
"type": "null"
}
],
"title": "Editor Code",
"default": null,
"description": "The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address."
}
}
}Play, pause or jump to at_ms in the user's editor, for example to have the user watch and feel one part. play and pause need a loaded video.
| Argument | Type | Description |
|---|---|---|
| at_ms | any | Start of the time range in milliseconds on the video timeline. |
| action* | string | |
| editor_code | any | The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address. |
{
"type": "object",
"title": "control_playbackArguments",
"required": [
"action"
],
"properties": {
"at_ms": {
"anyOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "null"
}
],
"title": "At Ms",
"default": null,
"description": "Start of the time range in milliseconds on the video timeline."
},
"action": {
"enum": [
"play",
"pause",
"seek"
],
"type": "string",
"title": "Action"
},
"editor_code": {
"anyOf": [
{
"type": "string",
"maxLength": 16
},
{
"type": "null"
}
],
"title": "Editor Code",
"default": null,
"description": "The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address."
}
}
}Render a described motion (or exact funscript points) and show it without playing it on a device. Needs no connection key. To play it afterwards, call play_motion with the same motion.
| Argument | Type | Description |
|---|---|---|
| title | any | Short name for the motion, for example Slow build-up. Used as the funscript title and download file name. |
| device | string | The device the motion is fitted to. |
| repeat | integer | Play the whole motion this many times in a row. |
| actions | any | Exact funscript points, for example from a pasted funscript. Use instead of sections. |
| sections | any | The motion as consecutive sections. Use this for described motion. |
{
"type": "object",
"$defs": {
"Action": {
"type": "object",
"title": "Action",
"required": [
"at",
"pos"
],
"properties": {
"at": {
"type": "number",
"title": "At",
"minimum": 0,
"description": "Time in milliseconds."
},
"pos": {
"type": "number",
"title": "Pos",
"maximum": 100,
"minimum": 0,
"description": "Position 0-100."
}
},
"description": "One funscript point."
},
"Section": {
"type": "object",
"title": "Section",
"required": [
"duration_seconds"
],
"properties": {
"top": {
"type": "number",
"title": "Top",
"default": 100,
"maximum": 100,
"minimum": 0,
"description": "Highest position of each stroke (100 = top of travel)."
},
"kind": {
"enum": [
"strokes",
"hold"
],
"type": "string",
"title": "Kind",
"default": "strokes",
"description": "strokes = repeated up-down strokes; hold = stay still at one position."
},
"curve": {
"enum": [
"snap",
"ease",
"settle",
"hold-peak",
"rise"
],
"type": "string",
"title": "Curve",
"default": "snap",
"description": "Stroke shape: snap (linear, crisp), ease (smooth), settle (lingers low), hold-peak (pauses briefly at the top), rise (slow rise, quick drop)."
},
"bottom": {
"type": "number",
"title": "Bottom",
"default": 0,
"maximum": 100,
"minimum": 0,
"description": "Lowest position of each stroke (0 = bottom of travel)."
},
"top_end": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Top End",
"default": null,
"description": "Highest position at the section end."
},
"position": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Position",
"default": null,
"description": "Hold sections only: the position to hold. Omit to hold where the motion stopped."
},
"variation": {
"type": "number",
"title": "Variation",
"default": 0,
"maximum": 1,
"minimum": 0,
"description": "Random variation in tempo and depth for a less mechanical feel. 0 = exact, 0.2-0.4 = natural."
},
"bottom_end": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Bottom End",
"default": null,
"description": "Lowest position at the section end."
},
"duration_seconds": {
"type": "number",
"title": "Duration Seconds",
"maximum": 3600,
"description": "Length of this section in seconds.",
"exclusiveMinimum": 0
},
"strokes_per_minute": {
"type": "number",
"title": "Strokes Per Minute",
"default": 60,
"maximum": 300,
"minimum": 1,
"description": "Tempo at the section start. One stroke is a full down-up-down cycle. Slow 20-40, medium 50-90, fast 100-160, very fast 170+."
},
"strokes_per_minute_end": {
"anyOf": [
{
"type": "number",
"maximum": 300,
"minimum": 1
},
{
"type": "null"
}
],
"title": "Strokes Per Minute End",
"default": null,
"description": "Tempo at the section end, to speed up or slow down gradually."
}
},
"description": "One stretch of motion. Fields ending in _end ramp linearly from the\nsection's start value to that value by the section's end."
}
},
"title": "create_motionArguments",
"properties": {
"title": {
"anyOf": [
{
"type": "string",
"maxLength": 80
},
{
"type": "null"
}
],
"title": "Title",
"default": null,
"description": "Short name for the motion, for example Slow build-up. Used as the funscript title and download file name."
},
"device": {
"enum": [
"handy",
"autoblow",
"vacuglide",
"bluetooth"
],
"type": "string",
"title": "Device",
"default": "handy",
"description": "The device the motion is fitted to."
},
"repeat": {
"type": "integer",
"title": "Repeat",
"default": 1,
"maximum": 100,
"minimum": 1,
"description": "Play the whole motion this many times in a row."
},
"actions": {
"anyOf": [
{
"type": "array",
"items": {
"$ref": "#/$defs/Action"
},
"maxItems": 10000
},
{
"type": "null"
}
],
"title": "Actions",
"default": null,
"description": "Exact funscript points, for example from a pasted funscript. Use instead of sections."
},
"sections": {
"anyOf": [
{
"type": "array",
"items": {
"$ref": "#/$defs/Section"
},
"maxItems": 200
},
{
"type": "null"
}
],
"title": "Sections",
"default": null,
"description": "The motion as consecutive sections. Use this for described motion."
}
}
}Build target_axis from source_axis over the whole script. method follow makes a scaled, optionally inverted copy shifted by delay_ms and position_offset, for example a gentler, delayed surge from the stroke. method per_stroke puts one point on each stroke of the source, moving between target_min (at the bottom) and target_max (at the top), best for vibrate.
| Argument | Type | Description |
|---|---|---|
| scale | number | |
| invert | boolean | |
| method | string | |
| delay_ms | number | |
| target_max | number | |
| target_min | number | |
| editor_code | any | The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address. |
| source_axis* | string | |
| target_axis* | string | |
| position_offset |
Change the chapters: add (name, from_ms, to_ms), rename (name, new_name), retime (name, from_ms, to_ms), remove (name) or clear_all. Chapters are found by exact name and overlapping neighbours are trimmed. Only when the user asks for a chapter change.
| Argument | Type | Description |
|---|---|---|
| name | any | |
| to_ms | any | End of the time range in milliseconds. |
| action* | string | |
| from_ms | any | Start of the time range in milliseconds on the video timeline. |
| new_name | any | |
| editor_code | any | The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address. |
{
"type": "object",
"title": "edit_chaptersArguments",
"required": [
"action"
],
"properties": {
"name": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"title": "Name",
"default": null
},
"to_ms": {
"anyOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "null"
}
],
"title": "To Ms",
"default": null,
"description": "End of the time range in milliseconds."
},
"action": {
"enum": [
"add",
"rename",
"retime",
"remove",
"clear_all"
],
"type": "string",
"title": "Action"
},
"from_ms": {
"anyOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "null"
}
],
"title": "From Ms",
"default": null,
"description": "Start of the time range in milliseconds on the video timeline."
},
"new_name": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"title": "New Name",
"default": null
},
"editor_code": {
"anyOf": [
{
"type": "string",
"maxLength": 16
},
{
"type": "null"
}
],
"title": "Editor Code",
"default": null,
"description": "The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address."
}
}
}Add, move or delete single points on one axis at exact times. Every other point stays as it is. add puts each point exactly where given and replaces an existing point within replace_within_ms of it. remove_at_ms deletes the point nearest each time, within replace_within_ms. To move a point, remove its old time and add the new one. Points are not speed fitted; the result lists any part now too fast for devices. Lands in the editor at once as one Undo step.
| Argument | Type | Description |
|---|---|---|
| add | any | Points to put at exact times. |
| axis | string | Axis. stroke is the main up and down axis. |
| editor_code | any | The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address. |
| remove_at_ms | any | Times in milliseconds of points to delete. |
| replace_within_ms | integer | How close counts as the same point. |
{
"type": "object",
"$defs": {
"Action": {
"type": "object",
"title": "Action",
"required": [
"at",
"pos"
],
"properties": {
"at": {
"type": "number",
"title": "At",
"minimum": 0,
"description": "Time in milliseconds."
},
"pos": {
"type": "number",
"title": "Pos",
"maximum": 100,
"minimum": 0,
"description": "Position 0-100."
}
},
"description": "One funscript point."
}
},
"title": "edit_pointsArguments",
"properties": {
"add": {
"anyOf": [
{
"type": "array",
"items": {
"$ref": "#/$defs/Action"
},
"maxItems": 1000
},
{
"type": "null"
}
],
"title": "Add",
"default": null,
"description": "Points to put at exact times."
},
"axis": {
"enum": [
"stroke",
"surge",
"sway",
"twist",
"roll",
"pitch",
"vibrate"
],
"type": "string",
"title": "Axis",
"default": "stroke",
"description": "Axis. stroke is the main up and down axis."
},
"editor_code": {
"anyOf": [
{
"type": "string",
"maxLength": 16
},
{
"type": "null"
}
],
"title": "Editor Code",
"default": null,
"description": "The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address."
},
"remove_at_ms": {
"anyOf": [
{
"type": "array",
"items": {
"type": "integer",
"minimum": 0
},
"maxItems": 1000
},
{
"type": "null"
}
],
"title": "Remove At Ms",
"default": null,
"description": "Times in milliseconds of points to delete."
},
"replace_within_ms": {
"type": "integer",
"title": "Replace Within Ms",
"default": 50,
"maximum": 1000,
"minimum": 0,
"description": "How close counts as the same point."
}
}
}The motion as a .funscript file with ScriptCompiler metadata, for the motion card's Download button.
| Argument | Type | Description |
|---|---|---|
| title | any | Short name for the motion, for example Slow build-up. Used as the funscript title and download file name. |
| device | string | The device the motion is fitted to. |
| repeat | integer | Play the whole motion this many times in a row. |
| actions | any | Exact funscript points, for example from a pasted funscript. Use instead of sections. |
| sections | any | The motion as consecutive sections. Use this for described motion. |
{
"type": "object",
"$defs": {
"Action": {
"type": "object",
"title": "Action",
"required": [
"at",
"pos"
],
"properties": {
"at": {
"type": "number",
"title": "At",
"minimum": 0,
"description": "Time in milliseconds."
},
"pos": {
"type": "number",
"title": "Pos",
"maximum": 100,
"minimum": 0,
"description": "Position 0-100."
}
},
"description": "One funscript point."
},
"Section": {
"type": "object",
"title": "Section",
"required": [
"duration_seconds"
],
"properties": {
"top": {
"type": "number",
"title": "Top",
"default": 100,
"maximum": 100,
"minimum": 0,
"description": "Highest position of each stroke (100 = top of travel)."
},
"kind": {
"enum": [
"strokes",
"hold"
],
"type": "string",
"title": "Kind",
"default": "strokes",
"description": "strokes = repeated up-down strokes; hold = stay still at one position."
},
"curve": {
"enum": [
"snap",
"ease",
"settle",
"hold-peak",
"rise"
],
"type": "string",
"title": "Curve",
"default": "snap",
"description": "Stroke shape: snap (linear, crisp), ease (smooth), settle (lingers low), hold-peak (pauses briefly at the top), rise (slow rise, quick drop)."
},
"bottom": {
"type": "number",
"title": "Bottom",
"default": 0,
"maximum": 100,
"minimum": 0,
"description": "Lowest position of each stroke (0 = bottom of travel)."
},
"top_end": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Top End",
"default": null,
"description": "Highest position at the section end."
},
"position": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Position",
"default": null,
"description": "Hold sections only: the position to hold. Omit to hold where the motion stopped."
},
"variation": {
"type": "number",
"title": "Variation",
"default": 0,
"maximum": 1,
"minimum": 0,
"description": "Random variation in tempo and depth for a less mechanical feel. 0 = exact, 0.2-0.4 = natural."
},
"bottom_end": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Bottom End",
"default": null,
"description": "Lowest position at the section end."
},
"duration_seconds": {
"type": "number",
"title": "Duration Seconds",
"maximum": 3600,
"description": "Length of this section in seconds.",
"exclusiveMinimum": 0
},
"strokes_per_minute": {
"type": "number",
"title": "Strokes Per Minute",
"default": 60,
"maximum": 300,
"minimum": 1,
"description": "Tempo at the section start. One stroke is a full down-up-down cycle. Slow 20-40, medium 50-90, fast 100-160, very fast 170+."
},
"strokes_per_minute_end": {
"anyOf": [
{
"type": "number",
"maximum": 300,
"minimum": 1
},
{
"type": "null"
}
],
"title": "Strokes Per Minute End",
"default": null,
"description": "Tempo at the section end, to speed up or slow down gradually."
}
},
"description": "One stretch of motion. Fields ending in _end ramp linearly from the\nsection's start value to that value by the section's end."
}
},
"title": "export_funscriptArguments",
"properties": {
"title": {
"anyOf": [
{
"type": "string",
"maxLength": 80
},
{
"type": "null"
}
],
"title": "Title",
"default": null,
"description": "Short name for the motion, for example Slow build-up. Used as the funscript title and download file name."
},
"device": {
"enum": [
"handy",
"autoblow",
"vacuglide",
"bluetooth"
],
"type": "string",
"title": "Device",
"default": "handy",
"description": "The device the motion is fitted to."
},
"repeat": {
"type": "integer",
"title": "Repeat",
"default": 1,
"maximum": 100,
"minimum": 1,
"description": "Play the whole motion this many times in a row."
},
"actions": {
"anyOf": [
{
"type": "array",
"items": {
"$ref": "#/$defs/Action"
},
"maxItems": 10000
},
{
"type": "null"
}
],
"title": "Actions",
"default": null,
"description": "Exact funscript points, for example from a pasted funscript. Use instead of sections."
},
"sections": {
"anyOf": [
{
"type": "array",
"items": {
"$ref": "#/$defs/Section"
},
"maxItems": 200
},
{
"type": "null"
}
],
"title": "Sections",
"default": null,
"description": "The motion as consecutive sections. Use this for described motion."
}
}
}See frames of the user's video as small pictures: one at at_ms, or up to 6 spread evenly from from_ms to to_ms. Use it to see what happens in a part, not to time single strokes. Only works after the user turns on Let my AI see video frames.
| Argument | Type | Description |
|---|---|---|
| at_ms | any | Start of the time range in milliseconds on the video timeline. |
| count | integer | |
| to_ms | any | End of the time range in milliseconds. |
| from_ms | any | Start of the time range in milliseconds on the video timeline. |
| editor_code | any | The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address. |
{
"type": "object",
"title": "look_at_videoArguments",
"properties": {
"at_ms": {
"anyOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "null"
}
],
"title": "At Ms",
"default": null,
"description": "Start of the time range in milliseconds on the video timeline."
},
"count": {
"type": "integer",
"title": "Count",
"default": 4,
"maximum": 6,
"minimum": 1
},
"to_ms": {
"anyOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "null"
}
],
"title": "To Ms",
"default": null,
"description": "End of the time range in milliseconds."
},
"from_ms": {
"anyOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "null"
}
],
"title": "From Ms",
"default": null,
"description": "Start of the time range in milliseconds on the video timeline."
},
"editor_code": {
"anyOf": [
{
"type": "string",
"maxLength": 16
},
{
"type": "null"
}
],
"title": "Editor Code",
"default": null,
"description": "The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address."
}
}
}Render a described motion (or exact funscript points) and play it on the user's device, replacing anything already playing. Returns what will actually play.
| Argument | Type | Description |
|---|---|---|
| title | any | Short name for the motion, for example Slow build-up. Used as the funscript title and download file name. |
| device | string | Which device the key belongs to. bluetooth covers every Bluetooth toy paired on the ToyController phone screen. |
| repeat | integer | Play the whole motion this many times in a row. |
| actions | any | Exact funscript points, for example from a pasted funscript. Use instead of sections. |
| sections | any | The motion as consecutive sections. Use this for described motion. |
| connection_key* | string | The device's connection key (Handy) or device token (Autoblow, VacuGlide) from the manufacturer's app, or the Device ID on the ToyController phone screen (Bluetooth toys). |
{
"type": "object",
"$defs": {
"Action": {
"type": "object",
"title": "Action",
"required": [
"at",
"pos"
],
"properties": {
"at": {
"type": "number",
"title": "At",
"minimum": 0,
"description": "Time in milliseconds."
},
"pos": {
"type": "number",
"title": "Pos",
"maximum": 100,
"minimum": 0,
"description": "Position 0-100."
}
},
"description": "One funscript point."
},
"Section": {
"type": "object",
"title": "Section",
"required": [
"duration_seconds"
],
"properties": {
"top": {
"type": "number",
"title": "Top",
"default": 100,
"maximum": 100,
"minimum": 0,
"description": "Highest position of each stroke (100 = top of travel)."
},
"kind": {
"enum": [
"strokes",
"hold"
],
"type": "string",
"title": "Kind",
"default": "strokes",
"description": "strokes = repeated up-down strokes; hold = stay still at one position."
},
"curve": {
"enum": [
"snap",
"ease",
"settle",
"hold-peak",
"rise"
],
"type": "string",
"title": "Curve",
"default": "snap",
"description": "Stroke shape: snap (linear, crisp), ease (smooth), settle (lingers low), hold-peak (pauses briefly at the top), rise (slow rise, quick drop)."
},
"bottom": {
"type": "number",
"title": "Bottom",
"default": 0,
"maximum": 100,
"minimum": 0,
"description": "Lowest position of each stroke (0 = bottom of travel)."
},
"top_end": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Top End",
"default": null,
"description": "Highest position at the section end."
},
"position": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Position",
"default": null,
"description": "Hold sections only: the position to hold. Omit to hold where the motion stopped."
},
"variation": {
"type": "number",
"title": "Variation",
"default": 0,
"maximum": 1,
"minimum": 0,
"description": "Random variation in tempo and depth for a less mechanical feel. 0 = exact, 0.2-0.4 = natural."
},
"bottom_end": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Bottom End",
"default": null,
"description": "Lowest position at the section end."
},
"duration_seconds": {
"type": "number",
"title": "Duration Seconds",
"maximum": 3600,
"description": "Length of this section in seconds.",
"exclusiveMinimum": 0
},
"strokes_per_minute": {
"type": "number",
"title": "Strokes Per Minute",
"default": 60,
"maximum": 300,
"minimum": 1,
"description": "Tempo at the section start. One stroke is a full down-up-down cycle. Slow 20-40, medium 50-90, fast 100-160, very fast 170+."
},
"strokes_per_minute_end": {
"anyOf": [
{
"type": "number",
"maximum": 300,
"minimum": 1
},
{
"type": "null"
}
],
"title": "Strokes Per Minute End",
"default": null,
"description": "Tempo at the section end, to speed up or slow down gradually."
}
},
"description": "One stretch of motion. Fields ending in _end ramp linearly from the\nsection's start value to that value by the section's end."
}
},
"title": "play_motionArguments",
"required": [
"connection_key"
],
"properties": {
"title": {
"anyOf": [
{
"type": "string",
"maxLength": 80
},
{
"type": "null"
}
],
"title": "Title",
"default": null,
"description": "Short name for the motion, for example Slow build-up. Used as the funscript title and download file name."
},
"device": {
"enum": [
"handy",
"autoblow",
"vacuglide",
"bluetooth"
],
"type": "string",
"title": "Device",
"default": "handy",
"description": "Which device the key belongs to. bluetooth covers every Bluetooth toy paired on the ToyController phone screen."
},
"repeat": {
"type": "integer",
"title": "Repeat",
"default": 1,
"maximum": 100,
"minimum": 1,
"description": "Play the whole motion this many times in a row."
},
"actions": {
"anyOf": [
{
"type": "array",
"items": {
"$ref": "#/$defs/Action"
},
"maxItems": 10000
},
{
"type": "null"
}
],
"title": "Actions",
"default": null,
"description": "Exact funscript points, for example from a pasted funscript. Use instead of sections."
},
"sections": {
"anyOf": [
{
"type": "array",
"items": {
"$ref": "#/$defs/Section"
},
"maxItems": 200
},
{
"type": "null"
}
],
"title": "Sections",
"default": null,
"description": "The motion as consecutive sections. Use this for described motion."
},
"connection_key": {
"type": "string",
"title": "Connection Key",
"maxLength": 255,
"minLength": 1,
"description": "The device's connection key (Handy) or device token (Autoblow, VacuGlide) from the manufacturer's app, or the Device ID on the ToyController phone screen (Bluetooth toys)."
}
}
}The points of one axis between from_ms and to_ms, thinned out evenly past 400 points, plus a summary of that range: main stroke frequency, position range and whether it builds or winds down.
| Argument | Type | Description |
|---|---|---|
| axis* | string | Axis. stroke is the main up and down axis. |
| to_ms* | integer | |
| from_ms* | integer | |
| editor_code | any | The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address. |
{
"type": "object",
"title": "read_pointsArguments",
"required": [
"axis",
"from_ms",
"to_ms"
],
"properties": {
"axis": {
"enum": [
"stroke",
"surge",
"sway",
"twist",
"roll",
"pitch",
"vibrate"
],
"type": "string",
"title": "Axis",
"description": "Axis. stroke is the main up and down axis."
},
"to_ms": {
"type": "integer",
"title": "To Ms",
"minimum": 0
},
"from_ms": {
"type": "integer",
"title": "From Ms",
"minimum": 0
},
"editor_code": {
"anyOf": [
{
"type": "string",
"maxLength": 16
},
{
"type": "null"
}
],
"title": "Editor Code",
"default": null,
"description": "The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address."
}
}
}Overview of the script open in the user's ScriptCompiler editor: each axis with its points, range, strokes and speed, parts too fast for devices (over 400 units per second), chapters, the video length, the play position, the user's selection, whether video frames are allowed, and the editor tools run_editor_tool can run. Call it first, and again after the user edits.
| Argument | Type | Description |
|---|---|---|
| editor_code | any | The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address. |
{
"type": "object",
"title": "read_scriptArguments",
"properties": {
"editor_code": {
"anyOf": [
{
"type": "string",
"maxLength": 16
},
{
"type": "null"
}
],
"title": "Editor Code",
"default": null,
"description": "The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address."
}
}
}Run one of the editor's own Tools panel tools (Smoothing, Speed Limiter, Simplify and the rest) on an axis, as if the user ran it. read_script lists the ids and settings. Give from_ms and to_ms for one range, or leave both out for the whole axis. A tool marked needs ScriptCompiler Pro cannot run: tell the user it is part of Pro and do not copy its effect another way. Lands as one Undo step.
| Argument | Type | Description |
|---|---|---|
| axis | string | Axis. stroke is the main up and down axis. |
| tool* | string | Tool id from read_script, for example smoothing. |
| to_ms | any | End of the time range in milliseconds. |
| params | any | The tool's settings by name. |
| from_ms | any | Start of the time range in milliseconds on the video timeline. |
| editor_code | any | The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address. |
{
"type": "object",
"title": "run_editor_toolArguments",
"required": [
"tool"
],
"properties": {
"axis": {
"enum": [
"stroke",
"surge",
"sway",
"twist",
"roll",
"pitch",
"vibrate"
],
"type": "string",
"title": "Axis",
"default": "stroke",
"description": "Axis. stroke is the main up and down axis."
},
"tool": {
"type": "string",
"title": "Tool",
"maxLength": 40,
"description": "Tool id from read_script, for example smoothing."
},
"to_ms": {
"anyOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "null"
}
],
"title": "To Ms",
"default": null,
"description": "End of the time range in milliseconds."
},
"params": {
"anyOf": [
{
"type": "object",
"additionalProperties": true
},
{
"type": "null"
}
],
"title": "Params",
"default": null,
"description": "The tool's settings by name."
},
"from_ms": {
"anyOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "null"
}
],
"title": "From Ms",
"default": null,
"description": "Start of the time range in milliseconds on the video timeline."
},
"editor_code": {
"anyOf": [
{
"type": "string",
"maxLength": 16
},
{
"type": "null"
}
],
"title": "Editor Code",
"default": null,
"description": "The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address."
}
}
}Stop whatever is playing on the user's device.
| Argument | Type | Description |
|---|---|---|
| device | string | Which device the key belongs to. bluetooth covers every Bluetooth toy paired on the ToyController phone screen. |
| connection_key* | string | The device's connection key (Handy) or device token (Autoblow, VacuGlide) from the manufacturer's app, or the Device ID on the ToyController phone screen (Bluetooth toys). |
{
"type": "object",
"title": "stop_motionArguments",
"required": [
"connection_key"
],
"properties": {
"device": {
"enum": [
"handy",
"autoblow",
"vacuglide",
"bluetooth"
],
"type": "string",
"title": "Device",
"default": "handy",
"description": "Which device the key belongs to. bluetooth covers every Bluetooth toy paired on the ToyController phone screen."
},
"connection_key": {
"type": "string",
"title": "Connection Key",
"maxLength": 255,
"minLength": 1,
"description": "The device's connection key (Handy) or device token (Autoblow, VacuGlide) from the manufacturer's app, or the Device ID on the ToyController phone screen (Bluetooth toys)."
}
}
}Undo the last change you made in the editor. It refuses when the user edited those axes since, so it never undoes the user's own work.
| Argument | Type | Description |
|---|---|---|
| editor_code | any | The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address. |
{
"type": "object",
"title": "undo_last_changeArguments",
"properties": {
"editor_code": {
"anyOf": [
{
"type": "string",
"maxLength": 16
},
{
"type": "null"
}
],
"title": "Editor Code",
"default": null,
"description": "The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address."
}
}
}Write several axes at once from one shared stroke rhythm, so all axes keep the same beat. schedule is an ordered, gap-free list of cycles (start_ms, duration_ms, kind stroke or hold, and for strokes bottom 0, top 100, peak_frac 0.5 and intensity 50 unless given). One stroke cycle is one full down and up. A hold pauses where the last cycle ended or at its position. Each axis spec is tracking (curve snap, ease, settle, hold-peak or rise, phase_lag 0-1, amplitude, baseline, per_cycle_track) or static (position, ease_in_ms). amplitude scales the schedule depth: the stroke axis always uses amplitude 50 and baseline 50, companions use a smaller amplitude. Set speed with duration_ms and depth with bottom and top. Pass target_duration_ms when the user asks for a length; the call fails if the schedule does not match it. Lands as one Undo step and returns measured stats.
| Argument | Type | Description |
|---|---|---|
| schedule* | array | |
| axis_specs* | array | |
| editor_code | any | The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address. |
| target_duration_ms | any |
{
"type": "object",
"$defs": {
"Cycle": {
"type": "object",
"title": "Cycle",
"required": [
"start_ms",
"duration_ms",
"kind"
],
"properties": {
"top": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Top",
"default": null
},
"kind": {
"enum": [
"stroke",
"hold"
],
"type": "string",
"title": "Kind"
},
"bottom": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Bottom",
"default": null
},
"position": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Position",
"default": null,
"description": "Hold cycles only: the 0-100 position to pause at. Omit to hold where the last cycle ended."
},
"start_ms": {
"type": "number",
"title": "Start Ms"
},
"intensity": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Intensity",
"default": null
},
"peak_frac": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Peak Frac",
"default": null
},
"duration_ms": {
"type": "number",
"title": "Duration Ms"
}
}
},
"AxisSpec": {
"type": "object",
"title": "AxisSpec",
"required": [
"axis",
"mode"
],
"properties": {
"axis": {
"enum": [
"stroke",
"surge",
"sway",
"twist",
"roll",
"pitch",
"vibrate"
],
"type": "string",
"title": "Axis"
},
"mode": {
"enum": [
"tracking",
"static"
],
"type": "string",
"title": "Mode"
},
"curve": {
"anyOf": [
{
"enum": [
"snap",
"ease",
"settle",
"hold-peak",
"rise"
],
"type": "string"
},
{
"type": "null"
}
],
"title": "Curve",
"default": null
},
"baseline": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Baseline",
"default": null
},
"position": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Position",
"default": null
},
"amplitude": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Amplitude",
"default": null
},
"phase_lag": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Phase Lag",
"default": null
},
"ease_in_ms": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Ease In Ms",
"default": null
},
"per_cycle_track": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"title": "Per Cycle Track",
"default": null
}
}
}
},
"title": "write_coordinated_axesArguments",
"required": [
"schedule",
"axis_specs"
],
"properties": {
"schedule": {
"type": "array",
"items": {
"$ref": "#/$defs/Cycle"
},
"title": "Schedule",
"maxItems": 5000,
"minItems": 1
},
"axis_specs": {
"type": "array",
"items": {
"$ref": "#/$defs/AxisSpec"
},
"title": "Axis Specs",
"maxItems": 7,
"minItems": 1
},
"editor_code": {
"anyOf": [
{
"type": "string",
"maxLength": 16
},
{
"type": "null"
}
],
"title": "Editor Code",
"default": null,
"description": "The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address."
},
"target_duration_ms": {
"anyOf": [
{
"type": "integer",
"minimum": 1
},
{
"type": "null"
}
],
"title": "Target Duration Ms",
"default": null
}
}
}Replace a time range on one axis with a described motion (sections, the same as play_motion) or with exact points. With sections the motion starts at from_ms and replaces what was there until it ends. With actions the points keep their own times, or are moved so the first one lands on from_ms. The motion is fitted to 400 units per second like the Speed Limiter unless fit_to_device_speed is false. Lands in the editor at once as one Undo step. To add, move or delete single points without replacing a range, use edit_points.
| Argument | Type | Description |
|---|---|---|
| axis | string | Axis. stroke is the main up and down axis. |
| repeat | integer | |
| actions | any | Exact points. |
| from_ms | any | Start of the time range in milliseconds on the video timeline. |
| sections | any | The motion as consecutive sections. |
| editor_code | any | The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address. |
| fit_to_device_speed | boolean |
{
"type": "object",
"$defs": {
"Action": {
"type": "object",
"title": "Action",
"required": [
"at",
"pos"
],
"properties": {
"at": {
"type": "number",
"title": "At",
"minimum": 0,
"description": "Time in milliseconds."
},
"pos": {
"type": "number",
"title": "Pos",
"maximum": 100,
"minimum": 0,
"description": "Position 0-100."
}
},
"description": "One funscript point."
},
"Section": {
"type": "object",
"title": "Section",
"required": [
"duration_seconds"
],
"properties": {
"top": {
"type": "number",
"title": "Top",
"default": 100,
"maximum": 100,
"minimum": 0,
"description": "Highest position of each stroke (100 = top of travel)."
},
"kind": {
"enum": [
"strokes",
"hold"
],
"type": "string",
"title": "Kind",
"default": "strokes",
"description": "strokes = repeated up-down strokes; hold = stay still at one position."
},
"curve": {
"enum": [
"snap",
"ease",
"settle",
"hold-peak",
"rise"
],
"type": "string",
"title": "Curve",
"default": "snap",
"description": "Stroke shape: snap (linear, crisp), ease (smooth), settle (lingers low), hold-peak (pauses briefly at the top), rise (slow rise, quick drop)."
},
"bottom": {
"type": "number",
"title": "Bottom",
"default": 0,
"maximum": 100,
"minimum": 0,
"description": "Lowest position of each stroke (0 = bottom of travel)."
},
"top_end": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Top End",
"default": null,
"description": "Highest position at the section end."
},
"position": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Position",
"default": null,
"description": "Hold sections only: the position to hold. Omit to hold where the motion stopped."
},
"variation": {
"type": "number",
"title": "Variation",
"default": 0,
"maximum": 1,
"minimum": 0,
"description": "Random variation in tempo and depth for a less mechanical feel. 0 = exact, 0.2-0.4 = natural."
},
"bottom_end": {
"anyOf": [
{
"type": "number",
"maximum": 100,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Bottom End",
"default": null,
"description": "Lowest position at the section end."
},
"duration_seconds": {
"type": "number",
"title": "Duration Seconds",
"maximum": 3600,
"description": "Length of this section in seconds.",
"exclusiveMinimum": 0
},
"strokes_per_minute": {
"type": "number",
"title": "Strokes Per Minute",
"default": 60,
"maximum": 300,
"minimum": 1,
"description": "Tempo at the section start. One stroke is a full down-up-down cycle. Slow 20-40, medium 50-90, fast 100-160, very fast 170+."
},
"strokes_per_minute_end": {
"anyOf": [
{
"type": "number",
"maximum": 300,
"minimum": 1
},
{
"type": "null"
}
],
"title": "Strokes Per Minute End",
"default": null,
"description": "Tempo at the section end, to speed up or slow down gradually."
}
},
"description": "One stretch of motion. Fields ending in _end ramp linearly from the\nsection's start value to that value by the section's end."
}
},
"title": "write_pointsArguments",
"properties": {
"axis": {
"enum": [
"stroke",
"surge",
"sway",
"twist",
"roll",
"pitch",
"vibrate"
],
"type": "string",
"title": "Axis",
"default": "stroke",
"description": "Axis. stroke is the main up and down axis."
},
"repeat": {
"type": "integer",
"title": "Repeat",
"default": 1,
"maximum": 100,
"minimum": 1
},
"actions": {
"anyOf": [
{
"type": "array",
"items": {
"$ref": "#/$defs/Action"
},
"maxItems": 10000
},
{
"type": "null"
}
],
"title": "Actions",
"default": null,
"description": "Exact points."
},
"from_ms": {
"anyOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "null"
}
],
"title": "From Ms",
"default": null,
"description": "Start of the time range in milliseconds on the video timeline."
},
"sections": {
"anyOf": [
{
"type": "array",
"items": {
"$ref": "#/$defs/Section"
},
"maxItems": 200
},
{
"type": "null"
}
],
"title": "Sections",
"default": null,
"description": "The motion as consecutive sections."
},
"editor_code": {
"anyOf": [
{
"type": "string",
"maxLength": 16
},
{
"type": "null"
}
],
"title": "Editor Code",
"default": null,
"description": "The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address."
},
"fit_to_device_speed": {
"type": "boolean",
"title": "Fit To Device Speed",
"default": true
}
}
}| number |
{
"type": "object",
"title": "derive_axisArguments",
"required": [
"source_axis",
"target_axis"
],
"properties": {
"scale": {
"type": "number",
"title": "Scale",
"default": 1
},
"invert": {
"type": "boolean",
"title": "Invert",
"default": false
},
"method": {
"enum": [
"follow",
"per_stroke"
],
"type": "string",
"title": "Method",
"default": "follow"
},
"delay_ms": {
"type": "number",
"title": "Delay Ms",
"default": 0
},
"target_max": {
"type": "number",
"title": "Target Max",
"default": 100,
"maximum": 100,
"minimum": 0
},
"target_min": {
"type": "number",
"title": "Target Min",
"default": 0,
"maximum": 100,
"minimum": 0
},
"editor_code": {
"anyOf": [
{
"type": "string",
"maxLength": 16
},
{
"type": "null"
}
],
"title": "Editor Code",
"default": null,
"description": "The editor code the user reads from the editor's AI panel under Your own AI, for example K7P4QX. Leave it out when connected with the account address."
},
"source_axis": {
"enum": [
"stroke",
"surge",
"sway",
"twist",
"roll",
"pitch",
"vibrate"
],
"type": "string",
"title": "Source Axis"
},
"target_axis": {
"enum": [
"stroke",
"surge",
"sway",
"twist",
"roll",
"pitch",
"vibrate"
],
"type": "string",
"title": "Target Axis"
},
"position_offset": {
"type": "number",
"title": "Position Offset",
"default": 0
}
}
}