Recipe catalog / finger-actions
Choose an action for every finger
On beat, which listed option should each finger in fingers take, given music and style, or rest?
A playing loop advances one subdivision at a time and wants every finger to pick from its own reachable options in a single call.
Explore this recipe interactively ยท Source and implementation guide
Use finger-actions in TypeScript
Install with npm install jev-recipes. Requires Node.js 22.9 or newer and ES modules. Set TYPESAFE_API_KEY in your server environment for live calls, which send input to TypeSafe and use API quota. See the installation guide.
import { fingerActions } from 'jev-recipes/finger-actions';
const result = await fingerActions({
"music": "Key C major, 72 bpm, gentle ballad. Last bar: right hand melody E4 D4 C4 (quarter, quarter, half) over a C major chord held in the left hand. The phrase just ended on the tonic.",
"beat": "Bar 5, eighth 1, start of a new phrase",
"fingers": [
{
"id": "rh-thumb",
"text": "Right thumb, resting on C4, free to move",
"options": [
{
"id": "c4",
"text": "Press C4 to restate the tonic"
},
{
"id": "hold",
"text": "Keep C4 sounding from the previous bar"
}
]
},
{
"id": "rh-index",
"text": "Right index, hovering over D4 and E4",
"options": [
{
"id": "e4",
"text": "Press E4 to start the melody a third above"
},
{
"id": "d4",
"text": "Press D4 to start stepwise"
}
]
},
{
"id": "lh-pinky",
"text": "Left pinky, on C3 which is currently sounding",
"options": [
{
"id": "c3",
"text": "Re-strike C3 for a fresh bass note"
},
{
"id": "g2",
"text": "Move down to G2 for a dominant pedal"
}
]
}
],
"style": "Keep the texture sparse; melody on top, single bass notes below.",
"minConfidence": 0.8
});
console.log(result);
Input contract
| Field | Type | Needed |
|---|---|---|
| music | string | Required |
| beat | string | Required |
| fingers | array | Required |
| style | string | Optional |
| minConfidence | number | Optional |
Full input and result schemas
{
"input": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"music": {
"type": "string"
},
"beat": {
"type": "string"
},
"fingers": {
"minItems": 1,
"maxItems": 20,
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"text": {
"type": "string"
},
"options": {
"minItems": 1,
"maxItems": 50,
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"id",
"text"
]
}
}
},
"required": [
"id",
"text",
"options"
]
}
},
"style": {
"type": "string"
},
"minConfidence": {
"type": "number",
"minimum": 0,
"maximum": 1
}
},
"required": [
"music",
"beat",
"fingers"
]
},
"result": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"model": {
"type": "string"
},
"usage": {
"type": "object",
"properties": {
"input_tokens": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"output_tokens": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
},
"required": [
"input_tokens",
"output_tokens"
],
"additionalProperties": false
},
"status": {
"type": "string",
"enum": [
"ready",
"review"
]
},
"assignments": {
"minItems": 1,
"maxItems": 20,
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"ready",
"review"
]
},
"verdict": {
"type": "string",
"enum": [
"chosen",
"rest",
"ambiguous"
]
},
"action": {
"type": [
"string",
"null"
]
},
"suggestedAction": {
"type": [
"string",
"null"
]
},
"confidence": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"probabilities": {
"type": "object",
"properties": {
"options": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "number",
"minimum": 0,
"maximum": 1
}
},
"rest": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"ambiguous": {
"type": "number",
"minimum": 0,
"maximum": 1
}
},
"required": [
"options",
"rest",
"ambiguous"
],
"additionalProperties": false
}
},
"required": [
"id",
"status",
"verdict",
"action",
"suggestedAction",
"confidence",
"probabilities"
],
"additionalProperties": false
}
}
},
"required": [
"model",
"usage",
"status",
"assignments"
],
"additionalProperties": false
}
}Saved example result
This hand-authored response demonstrates the contract. It is not a model accuracy measurement. Run it without an API key: npx jev-recipes demo finger-actions.
{
"model": "demo-fixture",
"usage": {
"input_tokens": 0,
"output_tokens": 0
},
"status": "ready",
"assignments": [
{
"id": "rh-thumb",
"status": "ready",
"verdict": "rest",
"action": null,
"suggestedAction": null,
"confidence": 0.84,
"probabilities": {
"options": {
"c4": 0.08,
"hold": 0.06
},
"rest": 0.84,
"ambiguous": 0.02
}
},
{
"id": "rh-index",
"status": "ready",
"verdict": "chosen",
"action": "e4",
"suggestedAction": "e4",
"confidence": 0.86,
"probabilities": {
"options": {
"e4": 0.86,
"d4": 0.1
},
"rest": 0.03,
"ambiguous": 0.01
}
},
{
"id": "lh-pinky",
"status": "ready",
"verdict": "chosen",
"action": "c3",
"suggestedAction": "c3",
"confidence": 0.9,
"probabilities": {
"options": {
"c3": 0.9,
"g2": 0.06
},
"rest": 0.03,
"ambiguous": 0.01
}
}
]
}
Evaluation evidence
No verified live accuracy measurement is available. Evaluate representative cases before using this decision in your workflow.
Use the evaluation guide to measure this decision on your own labeled cases.
Limitations
- Each finger is judged as its own question within one request, so two fingers can pick the same key. Resolve collisions and physical reach in code.
- Options are the caller-supplied reachable actions for that finger; the recipe does not invent notes or check key membership.
- Beat timing, note durations, and MIDI output belong in application code.
Related recipes
- next-note: Use next-note when the loop chooses one melodic note rather than an action per finger.
- next-chord: Use next-chord to pick the harmony first, then let finger-actions voice it.