Send feedback

Getting Started

Welcome to the Serenade docs! Here, you'll find an overview of everything Serenade can do, along with examples. First, let's get Serenade installed on your device.

Download

First, download and install Serenade. Serenade supports macOS, Windows, and Linux—platform-specific installation instructions can be found on the download page.

Setup

After installing Serenade, you'll be prompted to activate the app via an email link. If that link doesn't work, you can use the code provided on the activation page to activate manually.

After activation, Serenade can automatically install plugins for supported editors—Atom and VS Code. Make sure to restart your editor after the plugins are installed (to make sure the Serenade editor plugin is running), and you're ready to go!

You can use Serenade with your laptop microphone, though we recommend using a headset for the best accuracy. You can verify that a headset is being used by clicking , and then Settings.

Environment

Serenade floats above all your other windows, so you can keep it side-by-side with your editor. You can toggle Serenade by clicking the Listening switch or pressing Alt+Space. Then, as you speak, you'll see a list of transcripts in the Serenade window.

Sometimes, Serenade isn't sure what you said, so you'll see a few different options. The first one will be used automatically, but to use a different option (and undo the first one), just say the number you want to use instead. For instance, to use the second option, just say two. If none of the options are right, you can just say undo.

Concepts

Throughout the documentation, you'll see blocks that look like this:

Syntax

add [<type>] (function | method) <identifier>

Examples

voice
add method fly
before
class Bird:
    pass
after
class Bird:
    def fly(self):
        pass

Each block shows the syntax for the voice command followed by several examples. To change the programming language used in the examples, use the language switcher in the navigation bar.

  • Text in <angle brackets> is free-form text, like the name of a function or variable.
  • Text separated by pipes, like (foo | bar), represents a list of choices (i.e., an or).
  • Text in [square brackets] is optional.

Adding Code

In this section, we'll take a look at the various voice commands you can use for writing code, including insert, add, and type. As you'll see, the insert command is used to insert new code at the cursor position, while the add command is used to create new statements or larger constructs, like classes and functions.

Inserting Code

The insert command will insert code at the current cursor position, handling many formatting details for you. This command is useful for appending some text to an existing line of code.

Syntax

insert [above | below] <text>

Examples

voice
insert hello world
result
hello world
insert quotes hello comma world
value = 
value = "hello, world"
world
insert first equals array brackets zero
"first = array[0]"
insert value equals a plus b greater than three or not c
value = a + b > 3 || !c
voice
insert above hello
before
world
after
hello
world
insert below world
hello
hello
world

Functions & Methods

Syntax

add [<modifiers>] [<type>] (function | method) <identifier>

Examples

voice
add function compute factorial
result
def compute_factorial():
    pass
add method players
class Game:
    def score(self):
        return 5
class Game:
    def players(self):
        pass

    def score(self):
        return 5
add int method score
class Game:
    pass
class Game:
    def score(self) -> int:
        pass

Syntax

add type <type>

Examples

voice
add type str
before
def get_name():
    pass
after
def get_name() -> str:
    pass
add type int
def set_limit(limit):
    pass
def set_limit(limit: int):
    pass

To add parameter to a function definition, you can use add parameter:

Syntax

add [<type>] parameter <identifier>

Examples

voice
add parameter max speed
before
def run(distance):
    pass
after
def run(distance, max_speed):
    pass
add string parameter name
def say_hello():
    pass
def say_hello(name: str):
    pass
add parameter foo bar equals in quotes baz
def foo(a):
    pass
def foo(a, foo_bar="baz"):
    pass

To add an argument to a function call, you can use add argument:

Syntax

add argument <expression>

Examples

voice
add argument first name plus last name
before
print("hello")
after
print("hello", first_name + last_name)
add argument b equals one
foo(a)
foo(a, b=1)

You can also use lambda within any add command to create an inline anonymous function:

Syntax

add lambda <identifier> [of <identifier>]

Examples

voice
add lambda of e
result
lambda e: e

Classes

Syntax

add (class | enum) <identifier>

Examples

voice
add class download manager
result
class DownloadManager:
    pass
add enum category type
class CategoryType(enum.Enum):
    pass

Syntax

add property <identifier>

Examples

voice
add property players equals four
before
class Game:
    pass
after
class Game:
    players = 4

Syntax

add (extends | parent | superclass) <identifier>

Examples

voice
add parent animal
before
class Dog(Pet):
    pass
after
class Dog(Pet, Animal):
    pass

Conditions & Loops

Syntax

add (if | else if | while) <condition>

Examples

voice
add if
result
if True:
    pass
add if count equals ten
if count == 10:
    pass
add else if highest score plus one less than three
if True:
    pass
if True:
    pass
elif highest_score + 1 < 3:
    pass
add while false
while False:
    pass

Syntax

add (else | try | finally) <expression>

Examples

voice
add else
before
if False:
    pass
after
if False:
    pass
else:
    pass
add else return number times factorial foo of number minus one
if True:
    pass
if True:
    pass
else:
    return number * factorial_foo(number - 1)
add try
try:
    pass
except e:
    pass
add try write parens
try:
    write()
except e:
    pass
add finally
try:
    pass
except:
    pass
try:
    pass
except:
    pass
finally:
    pass

Syntax

add [catch | except] <expression>

Examples

voice
add except
before
try:
    pass
except:
    pass
after
try:
    pass
except:
    pass
except:
    pass
add except value error
try:
    pass
except:
    pass
try:
    pass
except:
    pass
except ValueError:
    pass

Syntax

add for [<expression> in <identifier>]

Examples

voice
add for i in range of five
result
for i in range(5):
    pass

Comments

Syntax

add comment <text>

Examples

voice
add comment fix later
result
# fix later

Expressions

Syntax

add import <identifier>

Examples

voice
add import time
before
import random
after
import random
import time
add import numpy as np
import numpy as np
add from flask import capital flask
from flask import Flask

Syntax

add [decorator | print | raise | return | return value | throw] <expression>

Examples

voice
add my value equals three
result
my_value = 3
add age equals current age plus one
age = current_age + 1
add decorator login required
@app.route("/")
def index():
    pass
@app.route("/")
@login_required
def index():
    pass
add print value
print(value)
add return random dot random parens
def get_random():
    pass
def get_random():
    return random.random()
add return value four
def get_random():
    return
def get_random():
    return 4
add raise e
raise e
add assert false
assert False
add string variable foo equals in quotes bar
foo: str = "bar"

Newlines

The newline command adds a new line at, above, or below the cursor.

Syntax

[add] newline [above | below]

Examples

voice
newline
before
one
two
three
after
one
t
wo
three
newline above
one
two
three
one

two
three
newline below
one
two
three
one
two

three

Raw Text

The type command inserts text at the current cursor position, without much formatting done for you. It's the simplest way to add text, and it can be useful if you want to manually specify formatting, spacing, etc.

Syntax

type [above | below] <text>

Examples

voice
type hello
result
hello
type camel case hello world
helloWorld
voice
type above hello
before
world
after
hello
world
type below world
hello
hello
world

The dictate command inserts text, but doesn't convert coding symbols to their equivalents. This can be useful for writing comments or docs, rather than code.

Syntax

dictate <text>

Examples

voice
dictate a plus b greater than not c
result
a plus b greater than not c

Finally, system command inserts text wherever your cursor has focus. For instance, if you're using VS Code and open a "Find" dialog, system will type into that find dialog rather than the main text editor.

Syntax

system <text>

Examples

voice
system hello world
result
hello world

Editing Code

In this section, we'll take a look at voice commands you can use for editing existing code.

Deleting

You can delete code with the delete command.

For a complete list of selectors, check out the selectors reference.

Syntax

delete <selector>

Examples

voice
delete line six
before
class Bird:
    def chirp(self):
        print("chirp")

    def fly(self):
        # TODO
        pass
after
class Bird:
    def chirp(self):
        print("chirp")

    def fly(self):
        pass
delete two methods
class Bird:
    def chirp(self):
        print("chirp")

    def fly(self):
        # TODO
        pass
class Bird:
    pass
delete lines five to seven
class Bird:
    def chirp(self):
        print("chirp")

    def fly(self):
        # TODO
        pass
class Bird:
    def chirp(self):
        print("chirp")

delete to end of word
class Bird:
    def chirp(self):
        print("chirp")

    def fly(self):
        # TODO
        pass
class B:
    def chirp(self):
        print("chirp")

    def fly(self):
        # TODO
        pass

Changing

The change command selects the nearest match to the cursor and replaces it with some text.

For a complete list of selectors, check out the selectors reference.

Syntax

change <selector> to <text>

Examples

voice
change fly to jump
before
def fly():
    pass
after
def jump():
    pass

You can also say 'rename' to change the name of a function, variable, class, or other named selector

Syntax

rename <selector> to <text>

Examples

voice
rename parameter to height
before
def fly(elevation):
    pass
after
def fly(height):
    pass
rename function fly to jump
def fly():
    pass
def jump():
    pass

Selecting

You can use commands likecopy, cut, select,paste and duplicate to manipulate text.

For a complete list of selectors, check out the selectors reference.

Syntax

(copy | cut | select) <selector>

Examples

copy first function
copy lines fifty to sixty
cut next class
cut next two words
select to end of block
select words one to three

Syntax

paste [above | below | inline]

Examples

paste
paste below

Duplicate copies your selection and pastes it above or below your cursor.

Syntax

duplicate <selector> [above | below]

Examples

duplicate line below
duplicate function foo above

Refactoring

Serenade has a number of powerful refactoring commands to edit your code quickly.

Syntax

(comment | uncomment) <selector>

Examples

voice
comment block
before
def check(x):
  if x < 3:
    return True

  return False
after
def check(x):
  # if x < 3:
  #  return True

  return False
uncomment lines two to three
url = "https://serenade.ai"
# pages = 3
# download(url, pages)
url = "https://serenade.ai"
pages = 3
download(url, pages)

Syntax

(indent | dedent) <selector>

Examples

voice
indent line
before
y = f(x)
z = g(y)
after
    y = f(x)
z = g(y)
dedent line
    y = f(x)
z = g(y)
y = f(x)
z = g(y)

Syntax

join <number> lines

Examples

voice
join lines
before
y = f(x)
z = g(y)
after
y = f(x)z = g(y)
join three lines
y = f(x)
z = g(y)
x = h(z)
y = f(x)z = g(y)x = h(z)

Symbols & Formatting

You'll often want to specify text formatting and add symbols to your code. In this section, we'll see how to do that with voice.

Text Formatting

By default, text will be lowercase, and words will be separated by spaces. To format text using camel case, underscores, etc., you can prefix any text with a style:

    Syntax

    [capital | camel case | pascal case | all caps | underscores | lowercase] <text>

    Examples

    insert sing song
    sing song
    insert capital sing song
    Sing song
    insert camel case sing song
    singSong
    insert pascal sing song
    SingSong
    insert all caps sing song
    SING_SONG
    insert underscores sing song
    sing_song
    add camel case my balance equals all caps starting balance plus one
    myBalance = STARTING_BALANCE + 1
    add argument pascal case some class
    create()
    create(SomeClass)

You can also style existing text by describing a style, followed by a selector to change:

Syntax

(capitalize | camel case | pascal case | all caps | underscores) <selector>

Examples

capitalize foo
foo
Foo
camel case next two words
sing song
singSong

Single Symbols

You can include symbols when speaking text in any insert, add, change, etc. command.

Syntax

<symbol>

Examples

insert foo dot bar
foo.bar
add if x plus y mod 2 is equal to 0
if x + y % 2 == 0:
    pass

Enclosure Symbols

Enclosure symbols can be used to wrap text. You can also dictate opening and closing enclosure symbols separately.

Syntax

[open | close | left | right] <enclosure symbols>

Examples

add f of n minus one
f(n - 1)
add in parens x plus y
(x + y)
insert double underscores main
__main__
add foo equals quotes bar
foo = 'bar'
type left brace five right brace
{5}

You can also surround a selection with enclosure symbols, or any symbol or text if you'd like

Syntax

surround <selector> with <symbols>

Examples

surround x plus y with parens
x + y
(x + y)
surround bar with quotes
foo = bar
foo = 'bar'
surround bar with asterisk
bar
*bar*

Escaping Symbols

If you need to use the literal text representation of a symbol (e.g., the word dash rather than the - character, you can escape it in a few ways:

Syntax

(escape | the word) <symbol>

Examples

type plus
+
type escape plus
plus
type equals
=
type the word equals
equals

System

Serenade can control applications even without a native plugin, from starting apps to pressing keys.

App Control

You can use Serenade to launch apps, switch windows, and close apps.

Launch an application

launch <text>

Examples

launch atom
launch slack

Bring an application to the foreground

focus <text>

Examples

focus chrome
focus code

Close an application

close <text>

Examples

close terminal
close music

On macOS, switching among windows of the current app

switch window

Pause Serenade

(pause | stop listening)

Sending Keystrokes

Serenade can also send keystrokes to any active application, even without a plugin.

Press a keyboard shortcut

press <key>

Examples

press enter
press command k

Type a string

type <text>

Examples

type g mail dot com
gmail.com

Manipulate tabs

(new | close | next | previous) tab

Trigger an undo

undo

Trigger a redo

redo

Run a command in a terminal

run

Workflow

In this section, we'll see how to use Serenade to control your editor, window, and system.

Editor Controls

You can control your editor window with Serenade, which includes opening files and navigating tabs.

Save the current file

save

Undo an operation

undo

Redo an operation

redo

Open a file with a name matching text

open <text>

Format the current file

style file

Create a new editor tab

new tab

Close the current editor tab

close tab

Switch to the next or previous tab

(next | previous) tab

Switch to a specific tab

tab (one | two | three ...)

Switch to a specific tab

(first | second | ...) tab

Debugging

Serenade integrates with the Visual Studio Code debugger, so you can run and debug your code with voice. (Support for IntelliJ and other JetBrains IDEs is coming soon!)

Add or remove a breakpoint

(add | remove | toggle) breakpoint

Start or stop a debugger session

(start | stop) (debugging | debugger)

Move the debugger step-by-step

step (into | out | over)

Continue until the next breakpoint

continue

Inspect a variable

inspect <selector>

Chaining

You can chain commands together to avoid having to pause between commands. For example, you can combine the end of file and paste command by saying end of file paste.

Repetition

Serenade supports several different options for repeating commands.

Syntax

(delete | paste | indent ...) <number> (times | <selectors>)

Examples

indent two times
y = f(x)
    y = f(x)
delete five characters
extraordinary
ordinary

Syntax

repeat [<text>]

Examples

repeat
repeat delete
repeat three times

Custom Commands

With the Serenade API, you can write your own voice commands. Custom commands are written via JavaScript files in your ~/.serenade/scripts directory. Each script in that directory will have access to a global object called serenade that serves as the entry point for the Serenade API. If you have any syntax errors in your scripts, they'll be displayed in the Serenade app.

Snippets

Snippets are a powerful way to create shortcuts for code you type regularly. To define new snippets, add them to ~/.serenade/scripts/snippets.js (you may need to create this file if it doesn't exist yet).

Here's an example of a snippet that creates a new Python method whose name is prefixed with test_.

serenade.language("python").snippet(
  "test method <%name%>",
  "def test_<%name%>(self):<%newline%><%indent%>pass",
  { "name": ["identifier", "underscores"] },
  "method"
);

Now, if you say test method foo, the following code will be generated:

def test_foo(self):
    pass

As you can see, the snippet method takes four parameters:

  • A string that specifies the trigger for the voice command. Surrounding text in <% %> creates a matching slot that matches any text. You can then reference the matched text in the generated snippets, much like regular expression capture groups.
  • A snippet to generate. If you defined a matching slot called <%name%> in the trigger, then <%name%> in the snippet will be replaced by whatever text was matched in the transcript.
  • A map of slots to styles. Styles describe how text should be formatted, and a slot can have multiple styles. For instance, if a slot represents an identifier (e.g., a class name) where symbols aren't allowed, and that identifier should be pascal case, then the values ["identifier", "pascal"] could be used.
  • How to add the snippet to your code. In the above example, we're specifying that this block should be added as a method, so if your cursor is outside of a class, it will move to the nearest class before inserting anything, just as it would if you said "add method". The default value for this argument is statement.

As another example, here's a snippet to add a new React class in a JavaScript file:

serenade.language("javascript").snippet(
  "new component <%name%>",
  "class <%name%><%cursor%> extends React.Component {<%newline%>}",
  { "identifier": ["identifier", "pascal"] }
);

Notice that you can use the special slot <%cursor%> to specify where the cursor will be placed after the snippet. The full list of special slots is:

  • <%cursor%>: Where the cursor will be placed after the snippet is added.
  • <%indent%>: One additional level of indentation.
  • <%newline%>: A newline.
  • <%terminator%>: The statement terminator for the current language, often a semicolon.

As one last example, here's a snippet to create a Java class with an extends and implements in one command:

serenade.language("java").snippet(
  "new class <%name%> extends <%extends%> implements <%implements%>",
  "public class <%name%><%cursor%> extends <%extends%> implements <%implements%> {<%newline%>}",
  {
    "name": ["pascal", "identifier"],
    "extends": ["pascal", "identifier"],
    "implements": ["pascal", "identifier"]
  },
  "class"
);

Workflow Automation

You can also use the Serenade API to automate workflows. To define new snippets, use the file ~/.serenade/scripts/custom.js (you may need to create this file if it doesn't exist yet).

For instance, to switch to the terminal and then run the command make clean && make, you can create a custom command that looks like this:

serenade.global().command("make", async api => {
  await api.focus("terminal");
  await api.typeText("make clean && make");
  await api.pressKey("return");
});

The global method specifies that we'd like this command to be triggerable from any application. The command method takes two arguments: the voice trigger for the command, followed by an asynchronous function to execute. An api instance is passed to your async function, which enables you to automate a workflow. Inside of that function, we're using the await keyword to make sure that each operation completes before moving onto the next one. Don't forget to make your function async via the async keyword!

Here's another example that finds text on the current page inside of Chrome when you say a command like find foo:

serenade.app("chrome").command("find <%text%>", async (api, matches) => {
  await api.pressKey("f", ["command"]);
  await api.typeText(matches.text);
});

This time, rather than specifying global(), we used .app("chrome") to make this command valid only when Google Chrome is focused. As with snippets, you can use slots to capture text. In this example, we used a slot called <%text%> to determine what to type into the Chrome search box.

Here's one last example that opens a terminal and then runs two different yarn commands:

serenade.global().command("yarn <%first%> then <%second%>", async (api, matches) => {
  await api.focus("term");
  await api.typeText("yarn " + matches.first);
  await api.pressKey("return");
  await api.wait(3000);
  await api.typeText("yarn " + matches.second);
  await api.pressKey("return");
});

As you can see, when you have multiple slots, the second argument to your callback will be a map, where the keys are the names of your slots, and the values are the matched text.

API Reference

Within your custom commands, you can use the Serenade API for even more control and flexibility.

class Serenade

Methods to create new Builder objects with either a global scope or scoped to a single application. You can access an instance of this class via the serenade global in any script.

class Builder

Methods to register new voice commands.

class API

Methods for workflow automation. An instance of API is passed as the first argument to the callback passed to the command method on a Builder.

Keys

You can speak any key name in order to reference it in a Serenade command.

Selectors Reference

A <selector> describes a block of code. By default, a selector applies to the nearest matching object. You can optionally apply previous or next to each selector, as in previous line or next line. Selectors can also include an ordinal prefix/postfix, as well as a range of objects.

Syntax

[to] [start of | end of | next | previous] [first | second | third ...] <object>

Examples

go to end of second line
delete next term
copy first function
cut previous argument
delete to end of line

Syntax

[to] [start of | end of | next | previous] <object> [one | two | three ...]

Examples

go to line fifty
cut parameter two
delete to end of line five

Syntax

[to] (next | previous | last) (one | two | three ...) <objects>

Examples

select to next parameter
copy previous three characters
delete last two lines

Syntax

<objects> (one | two | three ...) to (two | three | four ...)

Examples

copy functions one to five
delete blocks two to four

As you can see from the above examples, you can select objects based on raw text descriptions (like line or word) or code descriptions (like function or argument).

Tips and Tricks

  • Instead of worrying about spacing while writing code, use style file once your code is in a reasonable state, and Serenade will automatically format the entire file.
  • Long type or add commands can be less accurate than shorter commands. If you're seeing low accuracy, try breaking up longer commands into smaller, separate commands.
  • The type and add commands are similar, but have distinct use cases—add works best for writing new lines of code, including larger constructs like functions or classes, while type works best for inserting or appending text to an existing line.
  • Use copy/cut, paste, and go to liberally in order to more quickly move code around.
  • Make sure your system's microphone volume is set to the right level. We've found that around 80%, to make sure the input isn't too loud or too quiet, works best, but try moving it around if Serenade's results aren't accurate.
  • When using Serenade, try to speak conversationally, as though you were pair programming with someone seated next to you. Over-enunciating words can actually make Serenade less accurate!
  • If Serenade doesn't know a word, like numpy, you can spell it in a command, as with add import n u m p y.

FAQ