Storing hand-written notes on reMarkable (2023 update)

Picture of notebook with the heading "Notes" taken by David Travis, downloaded from Unsplash

About a year and a half ago, I received my reMarkable tablet and set out on building a workflow to convert my handwritten notes to text stored in Google Docs. A lot has changed since then, both in the software of the tablet, as well as my workflow. A colleague also suggested I post an update, so here we go.

Since I originally purchased the tablet, the team at reMarkable has released a series of updates to the tablet that has added and changed a lot of the functionality. I wrote the original tutorial on firmware 2.15.xxxx, and we're now at 3.3.xxxx. One of the biggest changes to my workflow, however, was the change to the convert-to-text function when you send your document via email. In the 3.3.x firmware, it no longer converts handwriting to text on demand when you email a notebook. You now have to explicitly convert the text, which makes for a slower experience. It also creates this text as a new page in your notebook. They've added a lot of editing features (as well as the new Type Folio) for this page, but it's far from a fully baked text editing experience, especially without having the new keyboard case.

Over this time, I realized Google Docs wasn't the best place for long term storage of notes that I want to reference. Discoverability took a dive as my notes increased. So where did I end up? Back at my old friend Evernote, taking advantage of a few old tricks that increase my ability to find and reference old notes.

As I noted in my previous post on this, the rest of this tutorial is going to assume you know JavaScript, as well as the "+" hack for aliasing your gmail account (that is, if your email is email@gmail.com, you can add a plus sign and any other characters to "email" and it will act as an alias that will show up in your main account. An example would be "email+notes@gmail.com").

If you're just interested in the code, here's a gist for ya!

the workflow

My workflow is only a slight bit more manual (due to the convert-to-text change I listed above) but the process is fairly the same. I convert my note to text, and email that new converted page as a text body to myself. From there, Google Apps Script looks for that specific address, parses my note for a title, notebook name, text body & reminders, and emails the result with a formatted subject line to my evernote account. On evernote, my note is sent to the right notebook as a new entry (or as an updated note: more on that below) and sets up the reminders I've flagged.

google script

The way we setup our google script is similar to before. We establish a few variables with a regex for parsing key metadata about the note: title, name of notebook, tags and reminder date, if any.

const NOTEBOOK_REGEX = /(\[@[a-zA-Z0-9 ]+\])+/g;
const TAGS_REGEX = /(#[ a-zA-Z0-9]+)+/g;
const TITLE_REGEX = /(=[a-zA-Z0-9 ]+)+/g;
const REMINDER_REGEX = /(\+[a-zA-Z0-9 \-\/]+)+/g;

/**
 * I expect the converted note to have the following:
 * 
 * [@name-of-notebook]
 * #tag #options
 * = title-of-note
 * + YYYY/MM/DD (for reminder date)
 **/

Next, like before, I set up some variables for the alias I send my notes to, the evernote email address, the label for archiving my email and our base query for Gmail.

const EMAIL_ALIAS = 'email+notes@gmail.com';
const EVERNOTE_EMAIL = "email-address@m.evernote.com"
const REMARKABLE_GMAIL_LABEL = GmailApp.getUserLabelByName('remarkable-stored')

const BASE_QUERY = `in:inbox to:${EMAIL_ALIAS} is:unread`;

Now, let's define our function. First, we use the GmailApp library in Google Apps Script to search our email using the query we defined.

function exportToEvernote() {
  const results = GmailApp.search(BASE_QUERY);
  
  // rest of logic
}

Now, let's map over the results and get the first message in each thread. From this message, we'll grab the content from the body of the email, and start to setup our parsing for metadata.

function exportToEvernote(){
  
  const BASE_QUERY = `in:inbox to:${EMAIL_ALIAS} is:unread`;
  const results = GmailApp.search(BASE_QUERY);

  results.map( thread => {
    const message = thread.getMessages()[0]
    
    // get content of note from the body
    const content = message.getPlainBody()

    // parse the notebook, title, tags, and reminders
    const notebookResult = content.match(NOTEBOOK_REGEX)
    const titleResult = content.match(TITLE_REGEX)
    const tagsResult = content.match(TAGS_REGEX)
    const reminderResult = content.match(REMINDER_REGEX)
    
    // ... logic ...
  }
}

I'll figure out a cleaner way to do this next step at some point. What we want to do is cleanup and format our metadata, so that the title, tags, notebook name, and reminder are formatted properly for emailing this note to Evernote. Evernote requires the subject line to be formatted as:

`Title of Note @Name-Of-Notebook #Tag #Names !ReminderDate`

Note that the @, # and ! are required for notebook, tags & reminders, respectively, if you enable Evernote to automatically file your notes. More info on that here.

function exportToEvernote(){
   
  // .. previous logic ...
   
  // The new code

    // filter out symbols from notebook name
    const name = notebookResult && 
                 notebookResult[0].replaceAll(/([\[\] ]+)+/g, "") || 
                 ""

    // filter out symbols from title
    const title = titleResult && 
                  titleResult[0].replaceAll(/(=[\[\] ]+)+/g, "") || 
                  ""

    // filter out spaces between word and # if any, join multiple as space delimited
    const tags = tagsResult && 
                 tagsResult.map(tag => tag.replace(/(#[ ])\w+/, "").toLowerCase()).join(" ") || ""

    // filter reminder and swap + for !
    const reminder = reminderResult && 
                     reminderResult[0].replace("+", "!").replace(" ", "").toLowerCase() || ""
    
    // set up evernote structured subject line
    const subject = `${title} ${reminder} ${name} ${tags} +`
    
    // .. rest of logic ...
}

Great. We have the content of the note set aside. We've formatted the subject line so Evernote can automatically file them. All that is left is to send to Evernote, and then mark the email as read and archive it (so it doesn't show up in our query again).

function exportToEvernote(){
    // ... previous logic ...

    MailApp.sendEmail(EVERNOTE_ADDRESS, subject, content)
    message.markRead()
    thread.addLabel(REMARKABLE_LABEL)
    thread.moveToArchive()
  })
}

Now let's put it all together:

/*
 * reMarkable Text to Evernote
 */

 
const EMAIL_ADDRESS = 'email+notes@gmail.com';
const EVERNOTE_ADDRESS = "email-address@m.evernote.com"

const REMARKABLE_LABEL = GmailApp.getUserLabelByName('remarkable-stored')

// regex for parsing
const NOTEBOOK_REGEX = /(\[@[a-zA-Z0-9 ]+\])+/g;
const TAGS_REGEX = /(#[ a-zA-Z0-9,.:;-]+)+/g;
const TITLE_REGEX = /(=[a-zA-Z0-9,. ]+)+/g;
const REMINDER_REGEX = /(\+[a-zA-Z0-9 \-\/]+)+/g;

function PushToEvernote(){
  const BASE_QUERY = `in:inbox to:${EMAIL_ALIAS} is:unread`;

  const results = GmailApp.search(BASE_QUERY);

  results.map( thread => {
    const message = thread.getMessages()[0]
    
    // get content of note from the body
    const content = message.getPlainBody()

    // parse the notebook, title, tags, and reminders
    const notebookResult = content.match(NOTEBOOK_REGEX)
    const titleResult = content.match(TITLE_REGEX)
    const tagsResult = content.match(TAGS_REGEX)
    const reminderResult = content.match(REMINDER_REGEX)

    // filter out symbols from notebook name
    const name = notebookResult && 
                 notebookResult[0].replaceAll(/([\[\] ]+)+/g, "") || 
                 ""

    // filter out symbols from title
    const title = titleResult && 
                  titleResult[0].replaceAll(/(=[\[\] ]+)+/g, "") || 
                  ""

    // filter out spaces between word and # if any, join multiple as space delimited
    const tags = tagsResult && 
                 tagsResult.map(tag => tag.replace(/(#[ ])\w+/, "").toLowerCase()).join(" ") || ""

    // filter reminder and swap + for !
    const reminder = reminderResult && 
                     reminderResult[0].replace("+", "!").replace(" ", "").toLowerCase() || ""

    // set up evernote structured subject line
    const subject = `${title} ${reminder} ${name} ${tags} +`

    MailApp.sendEmail(EVERNOTE_ADDRESS, subject, content)
    message.markRead()
    thread.addLabel(REMARKABLE_LABEL)
    thread.moveToArchive()
  })
}

And here's a screenshot of a note so you can see how I format the metadata in handwriting form:

Image of a grayscale e-ink tablet resting on top of a wooden outdoor patio table

And here's the Evernote results:

Screenshot of text note within the Evernote app on a computer screen

For now, this is scratching the itch that Google Drive left me with, and that is fast discovery of notes I want to frequently refer back to. I have some tweaks I want to add as well, namely:

  • Use symbols to denote if this note should be appended to a previous note of the same title within the same notebook
  • Make an easier system for parsing metadata (maybe handwritten in a front matter style?)
  • Make it easier to remove metadata (I mean, all those [@] and # don't really do anything for me when reading the document)
  • Explore using the Evernote API to link notes together a la Zettelkasten method
  • Explore other additions to the note ecosystem (I tried to find a safe way to go from reMarkable -> Obsidian, but the design of a such a thing is not worth the effort right now)

For those who use Notion, Coda or similar with an available API, this method is pretty adaptable. Maybe I'll whip something up for that... maybe.

so yeah, that's how I currently write, read, and reference my notes, using Google Apps Script as a bridge between my reMarkable and Evernote. Suggestions? Thoughts? Hit me on Twitter: @_jjphillips.