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 voice commands you can use for writing code. As you'll see, you can use the type command to insert text at the cursor, and the add command to create larger constructs like functions and loops.

Raw Text

The type command inserts text at the current cursor position, and it's the simplest way to add text. The type command is useful when you just want to insert plain text. For instance, you could use a type command to append text to the end of an existing line of code.

Syntax

type [above | below] <text>

Examples

voice
type hello
result
hello
type in quotes hello comma world
"hello, world"
type camel case hello world
helloWorld
type a plus b greater than three or not c
a + b > 3 || !c
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

Functions & Methods

Syntax

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

Examples

voice
add method fly
before
class Bird:
    pass
after
class Bird:
    def fly(self):
        pass
add string method chirp
class Bird:
    pass
class Bird:
    def chirp(self) -> str:
        pass
voice
add function average
result
def average():
    pass
add int function factorial
def factorial() -> int:
    pass

Syntax

add type <type>

Examples

voice
add type string
before
def name():
    return "Scout"
after
def name() -> str:
    return "Scout"

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

Syntax

add [<type>] parameter <identifier>

Examples

voice
add parameter speed
before
def run():
    pass
after
def run(speed):
    pass
add string parameter name
def say_hello():
    pass
def say_hello(name: string):
    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()
after
print(first_name + last_name)

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

Syntax

add lambda <identifier> [of <identifier>]

Examples

voice
add foo equals lambda of bar
result
foo = lambda bar: pass

Classes

Syntax

add (class | enum) <identifier>

Examples

voice
add class bird
result
class Bird:
    pass
add enum colors
class Colors(enum.Enum):
    pass

Syntax

add (extends | parent | superclass) <identifier>

Examples

voice
add extends animal
before
class Bird:
    pass
after
class Bird(Animal):
    pass
add parent animal
class Bird:
    pass
class Bird(Animal):
    pass

Conditions & Loops

Syntax

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

Examples

voice
add if n is less than two
result
if n < 2:
    pass
add else if n is less than ten
elif n < 10:
    pass
add while n is greater than ten
while n > 10:
    pass

Syntax

add (else | try | finally) <expression>

Examples

voice
add else return none
before
if a > b:
    pass
after
if a > b:
    pass
else:
    return None
add try x equals parse of y
try:
    x = parse(y)
except e:
    pass
add finally
try:
    pass
except:
    pass
try:
    pass
except:
    pass
finally:
    pass

Syntax

add [catch | except] <expression>

Examples

add catch e
try:
    pass
try:
    pass
except e:
    pass
add except e
try:
    pass
try:
    pass
except e:
    pass

Syntax

add for [<expression> in <identifier>]

Examples

voice
add for n in numbers
result
for n in numbers:
    pass

Comments

Syntax

add comment <text>

Examples

voice
add comment TODO
result
# TODO

Expressions

Syntax

add import <identifier>

Examples

voice
add import requests
result
import requests
add import numpy as np
import numpy as np

Syntax

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

Examples

voice
add height plus equals two
result
height += 2
add print height
print(height)
add throw e
raise e
add raise e
raise e
voice
add decorator with wings
before
def fly():
    pass
after
@with_wings
def fly():
    pass
add return value height
def fly():
    return
def fly():
    return height

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

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 fly(self):
    pass
delete method
class Bird:
  def chirp(self):
    print("chirp")

  def fly(self):
    # TODO
    pass
class Bird:
  def fly(self):
    # TODO
    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 fly(self):
    # TODO
    pass
delete next two methods
class Bird:
  def chirp(self):
    print("chirp")

  def fly(self):
    # TODO
    pass
class Bird:
  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
change parameter to height
def fly(elevation):
    pass
def fly(height):
    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

rename function fly to jump
def fly():
    pass
def jump():
    pass
rename parameter to height
def fly(elevation):
    pass
def fly(height):
    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] <text>

    Examples

    type sing song
    sing song
    type capital sing song
    Sing song
    type camel case sing song
    singSong
    type pascal case sing song
    SingSong
    type all caps sing song
    SING_SONG
    type 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:

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 type, add, change, etc. command.

Syntax

<symbol>

Examples

type 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)
type in double underscores main
__main__
add foo equals in quotes bar
foo = 'bar'
type left brace five right brace
{5}
type list open angle bracket string close angle bracket
list<string>

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

Syntax

surround <selector> with <enclosure symbols | symbol | text>

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 <symbol>

Examples

type plus
+
type escape plus
plus

Syntax

the word <symbol>

Examples

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/workflows.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