I built this because I wanted my handwritten notes to truly belong to me—free to be migrated to other apps instead of being locked inside the proprietary Goodnotes ecosystem.
To be honest, I have absolutely no background in reverse engineering or binary file parsing. I built this almost entirely through "vibe coding" (using LLMs as my pair programmers—mainly Gemini Pro/Flash, and occasionally Claude Sonnet).
My ultimate goal is to unlock .goodnotes files so they can be transferred, opened, and re-edited in other open-source note-taking software.
Here is the general flow of how the parsing works under the hood:
First, we blindly parse the .goodnotes ZIP archive using the underlying Protobuf Wire Format (since we obviously don't have the official .proto files). Inside, we found that the extracted data is compressed using Apple's proprietary Framed LZ4. After writing a decompressor for it, we get data serialized in Troy Hanson's TPL format. Finally, from this TPL data, we can extract the actual raw stroke information (X/Y points, pressure, and color) and render them out as scalable .svg files.
(You can check out the GitHub README and Wiki for a much more detailed technical breakdown of this process!)
I found the process of using LLMs to tackle a domain I knew nothing about fascinating. Happy to answer any questions about the parsing process or how I prompted the models to figure out the LZ4 frames!
Kaih1825•25m ago
I built this because I wanted my handwritten notes to truly belong to me—free to be migrated to other apps instead of being locked inside the proprietary Goodnotes ecosystem.
To be honest, I have absolutely no background in reverse engineering or binary file parsing. I built this almost entirely through "vibe coding" (using LLMs as my pair programmers—mainly Gemini Pro/Flash, and occasionally Claude Sonnet). My ultimate goal is to unlock .goodnotes files so they can be transferred, opened, and re-edited in other open-source note-taking software.
Here is the general flow of how the parsing works under the hood:
First, we blindly parse the .goodnotes ZIP archive using the underlying Protobuf Wire Format (since we obviously don't have the official .proto files). Inside, we found that the extracted data is compressed using Apple's proprietary Framed LZ4. After writing a decompressor for it, we get data serialized in Troy Hanson's TPL format. Finally, from this TPL data, we can extract the actual raw stroke information (X/Y points, pressure, and color) and render them out as scalable .svg files. (You can check out the GitHub README and Wiki for a much more detailed technical breakdown of this process!)
I found the process of using LLMs to tackle a domain I knew nothing about fascinating. Happy to answer any questions about the parsing process or how I prompted the models to figure out the LZ4 frames!
Here is the Web demo (also available on the GitHub repo): https://kaih1825.github.io/parser-for-goodnotes/?lang=en
Thanks a lot!