diff --git a/src/Lib/Ass/AssFactory.cc b/src/Lib/Ass/AssFactory.cc
index 334de5b8b3e1fc18514958faf63b3684aac84cc0..354c50d8ce4b7fca4b3bfb6d68924c93cfb3fd32 100644
--- a/src/Lib/Ass/AssFactory.cc
+++ b/src/Lib/Ass/AssFactory.cc
@@ -6,22 +6,19 @@ using namespace Vivy::Ass;
 bool
 AssFactory::initFromStorage() noexcept
 {
-    QTextStream in(&diskStorage);
     QString currentSection{};
     quint64 lineIndex = 0;
     QStringList stylesContent{};
     QStringList eventsContent{};
 
-    while (!in.atEnd()) {
-        const QString line = in.readLine().trimmed();
-
-        // Dectect comment
+    auto processLine = [&](const QString line) {
+        // Detect comment
         if (line.startsWith(";") || line.isEmpty()) {
             lineIndex++;
-            continue;
+            return;
         }
 
-        // Dectect sections
+        // Detect sections
         else if (line.startsWith("[") && line.endsWith("]")) {
             currentSection = line.mid(1, line.size() - 2);
             logDebug() << "Parsing section " << VIVY_LOG_QUOTED(currentSection);
@@ -57,7 +54,23 @@ AssFactory::initFromStorage() noexcept
         }
 
         lineIndex++;
-    }
+    };
+
+    if (internalAss.isArray()) {
+        QJsonArray arr = internalAss.toArray();
+        auto i         = arr.constBegin();
+        while (i != arr.constEnd()) {
+            processLine(i->toString());
+            ++i;
+        }
+    } else if (diskStorage.exists()) {
+        QTextStream in(&diskStorage);
+
+        while (!in.atEnd()) {
+            processLine(in.readLine().trimmed());
+        }
+    } else
+        return false;
 
     // Construct the styles and the lines
     try {
@@ -109,10 +122,24 @@ AssFactory::checkValidity() const noexcept
 
 AssFactory::AssFactory(const QString &fileName)
     : diskStorage(fileName)
+    , internalAss()
 {
     if (!diskStorage.open(QIODevice::ReadOnly | QIODevice::Text))
         throw std::runtime_error("failed to open file for reading");
 
+    init();
+}
+
+AssFactory::AssFactory(const QJsonValue &internal)
+    : diskStorage("")
+    , internalAss(internal)
+{
+    init();
+}
+
+void
+AssFactory::init()
+{
     if (!initFromStorage())
         throw std::runtime_error("failed to init ass factory from file");
 
@@ -142,6 +169,32 @@ AssFactory::getInfoSection() const noexcept
     return assInfo;
 }
 
+QStringList
+AssFactory::getAssHeaderAsText() const noexcept
+{
+    QStringList ret = QStringList("[" + sectionScriptInfo + "]");
+
+    auto it = assInfo.constBegin();
+    while (it != assInfo.constEnd()) {
+        ret += it.key() + ": " + it.value().toString();
+        ++it;
+    }
+
+    // TODO: add styles as text
+    ret.append("");
+    ret.append("[" + sectionStyles + "]");
+    //ret.append("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding");
+    for (int i = 0; i < assStyles.size(); ++i) {
+        ret.append(assStyles.at(i)->getRawText());
+    }
+
+    ret.append("");
+    ret.append("[" + sectionEvents + "]");
+    //ret.append("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
+
+    return ret;
+}
+
 StyleWeakPtr
 AssFactory::getStyle(const QString &name) const noexcept
 {
diff --git a/src/Lib/Ass/AssFactory.hh b/src/Lib/Ass/AssFactory.hh
index 8e1812889b07cf4fb7183943662d6a6fb56d0b54..9e0c49c513a95ec82cb91b5b0256e893e1c67f1d 100644
--- a/src/Lib/Ass/AssFactory.hh
+++ b/src/Lib/Ass/AssFactory.hh
@@ -23,6 +23,8 @@ public:
 
 private:
     QFile diskStorage;
+    QJsonValue internalAss;
+
     SectionContent assInfo{};
     QVector<LinePtr> assLines{};
     QVector<StylePtr> assStyles{};
@@ -44,14 +46,18 @@ private:
     bool initFromStorage() noexcept;
     bool checkValidity() const noexcept;
 
+    void init();
+
 public:
     explicit AssFactory(const QString &);
+    explicit AssFactory(const QJsonValue &);
     ~AssFactory() noexcept = default;
 
     bool hasStyle(const QString &) const noexcept;
     StyleWeakPtr getStyle(const QString &) const noexcept;
 
     SectionContent getInfoSection() const noexcept;
+    QStringList getAssHeaderAsText() const noexcept;
     void getStyles(QVector<StylePtr> &) const noexcept;
     void getLines(QVector<LinePtr> &) const noexcept;
 };
diff --git a/src/Lib/Ass/Line.cc b/src/Lib/Ass/Line.cc
index 5188678d6740670cc6ff68299fd5a5b8d6f1553f..af9448cb6ca28372faf56104ff0fad8025d07471 100644
--- a/src/Lib/Ass/Line.cc
+++ b/src/Lib/Ass/Line.cc
@@ -180,7 +180,10 @@ Line::getContentAsText() const noexcept
 QString
 Line::getLineAsText() const noexcept
 {
-    return QString::asprintf("%s: %d,%s,%s,", "Dialogue", layer, "0:00:00.00", "0:00:00.00") +
+    // FIXME: asprintf bad, use QString("%x").arg()?
+    return QString::asprintf("%s: %d,%s,%s,", "Dialogue", layer,
+                             Utils::Time::fromUInt(start).toString().toStdString().c_str(),
+                             Utils::Time::fromUInt(end).toString().toStdString().c_str()) +
            styleProperties.name +
            QString::asprintf(",%s,%d,%d,%d,%s,", "", styleProperties.marginL,
                              styleProperties.marginR, styleProperties.marginV, "karaoke") +
diff --git a/src/Lib/Ass/Style.cc b/src/Lib/Ass/Style.cc
index 0a78af9537991cd7d4f8d123ea79b3034967bcc8..c6114cfc31a49dd72e66771bb9200aab4e299dc2 100644
--- a/src/Lib/Ass/Style.cc
+++ b/src/Lib/Ass/Style.cc
@@ -196,3 +196,21 @@ Style::getProperties() const noexcept
     ret.setObject(object);
     return ret;
 }
+
+#define PRINT_STYLE_BOOL(b) b ? -1 : 0
+QString
+Style::getRawText() const noexcept
+{
+    // FIXME: asprintf bad, use QString("%x").arg()?
+    return QString::asprintf(
+        "Style: %s,%s,%d,%s,%s,%s,%s,%d,%d,%d,%d,%0.2f,%0.2f,%0.2f,%0.2f,%0.2f,%0.2f,%0.2f,%d,%d,%d,%d,%d",
+        styleName.toStdString().c_str(), fontName.toStdString().c_str(), fontSize,
+        Color::toString(primaryColor).toStdString().c_str(),
+        Color::toString(secondaryColor).toStdString().c_str(),
+        Color::toString(outlineColor).toStdString().c_str(),
+        Color::toString(backColor).toStdString().c_str(), PRINT_STYLE_BOOL(bold),
+        PRINT_STYLE_BOOL(italic), PRINT_STYLE_BOOL(underline), PRINT_STYLE_BOOL(strikeOut),
+        double(scaleX), double(scaleY), double(spacing), double(angle), double(borderStyle),
+        double(outline), double(shadow), alignment, marginL, marginR, marginV, encoding);
+}
+#undef PRINT_STYLE_BOOL
diff --git a/src/Lib/Ass/Style.hh b/src/Lib/Ass/Style.hh
index 6712368c054861e3f03368a3f27c4002f7166da6..d4b3bfb62c679466988af0831261edd8da927535 100644
--- a/src/Lib/Ass/Style.hh
+++ b/src/Lib/Ass/Style.hh
@@ -47,5 +47,6 @@ public:
 
     QString getElementName() const noexcept;
     QJsonDocument getProperties() const noexcept;
+    QString getRawText() const noexcept;
 };
 }
diff --git a/src/Lib/Ass/StyleProperties.hh b/src/Lib/Ass/StyleProperties.hh
index d01680a297640f9f2bbdbbd42a5884ea59584264..0efc92abddef11e533d97b18790a50866a00e944 100644
--- a/src/Lib/Ass/StyleProperties.hh
+++ b/src/Lib/Ass/StyleProperties.hh
@@ -8,7 +8,7 @@ namespace Vivy::Ass
 // Overrides some properties of the Style of an Ass::Line, Ass::Char, Ass::Syl
 
 struct StyleProperties final {
-    QString name{};
+    QString name{ "Default" };
 
     // Colors
     QColor primaryColor{ Color::defaultValue }, secondaryColor{ Color::defaultValue },
diff --git a/src/Lib/Document/CRTPSubDocument.hh b/src/Lib/Document/CRTPSubDocument.hh
index 7aea6cf7871c4a7caf0167ac79f998ad16e38de6..96a2607b12abe7a5299250fe90b955aa9ba75629 100644
--- a/src/Lib/Document/CRTPSubDocument.hh
+++ b/src/Lib/Document/CRTPSubDocument.hh
@@ -129,7 +129,27 @@ class AssSubDocument final : public CRTPSubDocument<AssDocumentType, AssSubDocum
     static constexpr inline bool hasContext = false;
     const QStringList &suffixList           = Vivy::Utils::assFileSuffix;
 
+public:
+    static std::unique_ptr<AssSubDocument> fromInternal(const QJsonValue &internal) noexcept
+    {
+        auto ret = std::unique_ptr<AssSubDocument>(new AssSubDocument());
+        ret->logDebug() << "Init ASS subdocument from internal";
+
+        try {
+            ret->initFromInternal(internal); // May throw
+        } catch (const std::runtime_error &e) {
+            ret->logDebug() << "Failed to init ASS subdocument from internal: " << e.what();
+            ret.reset();
+            // FIXME: currently terminate, but should be changed to stop the loading of the document
+            throw e;
+        }
+
+        return ret;
+    }
+
+private:
     void initFromPath(const QString &);
+    void initFromInternal(const QJsonValue &);
 
     explicit AssSubDocument() noexcept = default;
     friend CRTPSubDocument<AssDocumentType, AssSubDocument, void>;
@@ -143,9 +163,14 @@ public:
     const QVector<Ass::LinePtr> &getLines() const noexcept;
     const QVector<Ass::StylePtr> &getStyles() const noexcept;
 
+    void setFilePath(QString filepath) noexcept;
+    void setInternalAss(bool b) noexcept;
+    bool haveInternalAss() const noexcept;
+
 private:
     QVector<Ass::StylePtr> styles;
     QVector<Ass::LinePtr> lines;
     Ass::AssFactory *assFactory;
+    bool internalAss{ false };
 };
 }
diff --git a/src/Lib/Document/CRTPSubDocument/AssSubDocument.cc b/src/Lib/Document/CRTPSubDocument/AssSubDocument.cc
index 9f55a6f3a722b98accb463def72b8ba59e1a7af2..96366e4a6ac7b2d7b0a4ddea82b3eb9323014f37 100644
--- a/src/Lib/Document/CRTPSubDocument/AssSubDocument.cc
+++ b/src/Lib/Document/CRTPSubDocument/AssSubDocument.cc
@@ -15,6 +15,16 @@ AssSubDocument::initFromPath(const QString &path)
     assFactory->getLines(lines);
 }
 
+void
+AssSubDocument::initFromInternal(const QJsonValue &internal)
+{
+    styles.clear();
+    lines.clear();
+    assFactory = new Ass::AssFactory(internal);
+    assFactory->getStyles(styles);
+    assFactory->getLines(lines);
+}
+
 QString
 AssSubDocument::getElementName() const noexcept
 {
@@ -53,14 +63,36 @@ AssSubDocument::getFactory() noexcept
     return assFactory;
 }
 
+bool
+AssSubDocument::haveInternalAss() const noexcept
+{
+    return internalAss;
+}
+
+void
+AssSubDocument::setInternalAss(bool b) noexcept
+{
+    internalAss = b;
+}
+
 QJsonArray
 AssSubDocument::getInternalAss() noexcept
 {
     QJsonArray ret;
 
+    QStringList info = assFactory->getAssHeaderAsText();
+    for (int i = 0; i < info.size(); ++i)
+        ret.append(info.at(i));
+
     for (int i = 0; i < lines.size(); i++) {
         ret.append(lines[i]->getLineAsText());
     }
 
     return ret;
 }
+
+void
+AssSubDocument::setFilePath(QString filepath) noexcept
+{
+    filePath = filepath;
+}
diff --git a/src/Lib/Document/VivyDocument.cc b/src/Lib/Document/VivyDocument.cc
index baf0113ea79a285a7a690b4ddcd3e874d9f38e8d..8dc4064c017be92d214df16c30e65e9320c3f269 100644
--- a/src/Lib/Document/VivyDocument.cc
+++ b/src/Lib/Document/VivyDocument.cc
@@ -89,6 +89,26 @@ VivyDocument::loadSaveJsonDocumentFile_ALPHA(VivyDocument *const self, const QJs
         if (!self->loadSubDocument(ass.toString(), Capabilities::AssAble))
             throw std::runtime_error("Failed to load ASS sub document");
     }
+
+    /*
+     * TODO: for now, if there is an internal ass we use it and ignore the SubAss file
+     * In the future, both should be loaded to allow the user to switch between the 2
+     * (e.g. a "reference" ass and the vivy ass)
+     */
+    if (QJsonValue haveInternalAss = json[KeySubDocuments][KeyHaveInternalAss];
+        !haveInternalAss.isUndefined() && haveInternalAss.toBool()) {
+        // ASS is inside Vivy document
+        if (QJsonValue internalAssSource = json[KeySubDocuments][KeyInternalAssSource];
+            !internalAssSource.isUndefined() && internalAssSource.isArray()) {
+            if (!self->loadSubDocument(internalAssSource, Capabilities::AssAble)) {
+                throw std::runtime_error("Failed to load ASS sub document");
+            }
+        }
+    } else if (QJsonValue ass = json[KeySubDocuments][KeySubAss]; !ass.isUndefined()) {
+        // ASS in its own ASS file
+        if (!self->loadSubDocument(ass.toString(), Capabilities::AssAble))
+            throw std::runtime_error("Failed to load ASS sub document");
+    }
 }
 
 void
@@ -130,13 +150,12 @@ VivyDocument::getSaveJsonDocumentFile() const
         subDocumentJson.insert(KeySubVideo, videoDocument->getFilePath());
 
     if (documentType & Capabilities::AssAble) {
+        subDocumentJson.insert(KeySubAss, assDocument->getFilePath());
+        subDocumentJson.insert(KeyHaveInternalAss, assDocument->haveInternalAss());
+
         // ASS is inside Vivy document
-        if ((assDocument->getFilePath() == getName()))
+        if (assDocument->haveInternalAss())
             subDocumentJson.insert(KeyInternalAssSource, assDocument->getInternalAss());
-
-        // ASS is in its own ASS file
-        else
-            subDocumentJson.insert(KeySubAss, assDocument->getFilePath());
     }
 
     json.insert(KeySubDocuments, subDocumentJson);
@@ -203,6 +222,26 @@ VivyDocument::loadSubDocument(const QString &subName, VivyDocument::Capabilities
     return true;
 }
 
+bool
+VivyDocument::loadSubDocument(const QJsonValue &internalAss,
+                              VivyDocument::Capabilities asType) noexcept
+{
+    logDebug() << "ASS sub-doc: Trying to open file internal ASS";
+
+    if (asType == Capabilities::AssAble) {
+        if ([[maybe_unused]] auto doc = assDocument.get()) {
+            // Here we may want to do some confirmation / cleanup
+            assDocument.reset();
+        }
+
+        assDocument = AssSubDocument::fromInternal(internalAss);
+        if (assDocument)
+            addDocumentType(AssAble);
+    }
+
+    return true;
+}
+
 bool
 VivyDocument::detectDocumentType(const QFileInfo &file, Capabilities *const ableType) noexcept
 {
diff --git a/src/Lib/Document/VivyDocument.hh b/src/Lib/Document/VivyDocument.hh
index 37448b56bc833252086505dfb6f850700ab961cb..e2f4d25bb9cf61dafe438881ae73cd37228070f2 100644
--- a/src/Lib/Document/VivyDocument.hh
+++ b/src/Lib/Document/VivyDocument.hh
@@ -26,6 +26,7 @@ class VivyDocument final : public AbstractDocument {
     DCL_VIVY_SAVE_KEY(SubAss)
     DCL_VIVY_SAVE_KEY(SubVideo)
     DCL_VIVY_SAVE_KEY(InternalAssSource)
+    DCL_VIVY_SAVE_KEY(HaveInternalAss)
     DCL_VIVY_SAVE_KEY(DocumentVersion)
 
     using LoadFunctionForVersion =
@@ -82,6 +83,7 @@ public:
     void save() override;
     bool loadSubDocument(const QString &) noexcept;
     bool loadSubDocument(const QString &, Capabilities) noexcept;
+    bool loadSubDocument(const QJsonValue &, VivyDocument::Capabilities) noexcept;
 
     // Getters
     std::shared_ptr<AudioSubDocument> getAudioSubDocument() const noexcept;
diff --git a/src/Lib/Utils.cc b/src/Lib/Utils.cc
index 5bfeaab70b7818aa03d88bf61f8e7204c7989e2a..408a13a9d0edb749fffcd470e016b64d27d6a6f0 100644
--- a/src/Lib/Utils.cc
+++ b/src/Lib/Utils.cc
@@ -146,6 +146,25 @@ Utils::Time::fromString(const QString &str)
         throw std::runtime_error("The string is not of the format `H:MM:SS.cs`");
 }
 
+Utils::Time
+Utils::Time::fromUInt(const quint64 &t)
+{
+    quint64 rem = t;
+    Time ret;
+
+    ret.hour = rem / (100 * 60 * 60);
+    rem      = rem % (100 * 60 * 60);
+
+    ret.minute = rem / (100 * 60);
+    rem        = rem % (100 * 60);
+
+    ret.second = rem / (100);
+
+    ret.centisecond = rem % 100;
+
+    return ret;
+}
+
 quint64
 Utils::Time::toUInt() const noexcept
 {
@@ -156,8 +175,10 @@ Utils::Time::toUInt() const noexcept
 QString
 Utils::Time::toString() const noexcept
 {
-    return QString::number(hour) + ":" + QString::number(minute) + ":" + QString::number(second) +
-           "." + QString::number(centisecond);
+    return QString("%1").arg(hour, 2, 10, QChar('0')) + ":" +
+           QString("%1").arg(minute, 2, 10, QChar('0')) + ":" +
+           QString("%1").arg(second, 2, 10, QChar('0')) + "." +
+           QString("%1").arg(centisecond, 2, 10, QChar('0'));
 }
 
 QString
diff --git a/src/Lib/Utils.hh b/src/Lib/Utils.hh
index 097b16ad332a0335926063f990090d6372ae40ff..d80030d0aae5ea7e1043a540ab64c297de89b429 100644
--- a/src/Lib/Utils.hh
+++ b/src/Lib/Utils.hh
@@ -217,6 +217,7 @@ struct Time final {
     quint64 centisecond;
 
     static Time fromString(const QString &);
+    static Time fromUInt(const quint64 &);
 
     QString toString() const noexcept;
     quint64 toUInt() const noexcept;
diff --git a/src/UI/MainWindow.cc b/src/UI/MainWindow.cc
index a877dcc60bd6118a41e895cb8e170e9e12c227ac..a2bf30110b1979e046f8c7877c37948fc31280da 100644
--- a/src/UI/MainWindow.cc
+++ b/src/UI/MainWindow.cc
@@ -47,6 +47,7 @@ MainWindow::MainWindow() noexcept
     DCL_MENU(edit, "&Edit");
     DCL_MENU(viewTmp, "&View");
     viewMenu = viewTmpMenu; // Save the view menu
+    DCL_MENU(ass, "&ASS");
     DCL_MENU(simulate, "&Simulate");
     DCL_MENU(help, "&Help");
 
@@ -60,6 +61,8 @@ MainWindow::MainWindow() noexcept
     DCL_ACTION(loadSubDocumentVideo, "&Load video", "Load a vide file as a sub-document", file);
     DCL_ACTION(loadSubDocumentAss, "&Load ASS", "Load an ASS file as a sub-document", file);
 
+    DCL_ACTION(setAssInternal, "&Internal ASS", "Set the ASS subdocument as internal", ass);
+
     DCL_ACTION(openDialogHelp, "&About", "Open the help dialog", help);
 
     ACTION_ADD_ICON(newDocument, VIVY_ICON_NEW);
@@ -205,6 +208,23 @@ MainWindow::openProperties(int index) noexcept
     current->openProperties();
 }
 
+void
+MainWindow::setAssInternal() noexcept
+{
+    try {
+        const auto document = dynamic_cast<VivyDocument *>(getCurrentDocument());
+        if (document) {
+            if (auto p = document->getAssSubDocument()) {
+                p->setInternalAss(true);
+            }
+        }
+    }
+
+    catch (const std::runtime_error &e) {
+        logError() << "Failed to set ASS as internal: " << e.what();
+    }
+}
+
 void
 MainWindow::openDialogHelp() noexcept
 {
diff --git a/src/UI/MainWindow.hh b/src/UI/MainWindow.hh
index 690ed2f9b0f3f49b53d5779c7c198bd34a2f49a7..bc59b3ce87f516f6e5079a7cd4c31addcf4028f8 100644
--- a/src/UI/MainWindow.hh
+++ b/src/UI/MainWindow.hh
@@ -117,6 +117,8 @@ private slots:
     void saveFileAs() noexcept;
     void renameFile() noexcept;
 
+    void setAssInternal() noexcept;
+
     void openDialogHelp() noexcept;
 
     void documentViewActionsChanged() noexcept;