diff --git a/templates/playground/playground.tmpl b/templates/playground/playground.tmpl
index 183e31d213..099273578c 100644
--- a/templates/playground/playground.tmpl
+++ b/templates/playground/playground.tmpl
@@ -4,4 +4,5 @@ User : {{.UserName}}
{{else}}
User : None
{{end}}
+
{{template "base/footer" .}}
diff --git a/web_src/js/components/Playground.vue b/web_src/js/components/Playground.vue
new file mode 100644
index 0000000000..7d239c085b
--- /dev/null
+++ b/web_src/js/components/Playground.vue
@@ -0,0 +1,41 @@
+
+
+ {{ gameData.File.Name }}
+
+ {{ gameData.File.name }}
+
+
+
+ {{ serverData.data }}
+
+
+
+
+
diff --git a/web_src/js/components/Step.vue b/web_src/js/components/Step.vue
new file mode 100644
index 0000000000..add7446d78
--- /dev/null
+++ b/web_src/js/components/Step.vue
@@ -0,0 +1,33 @@
+
+
+ {{ StepName }} : {{ stepData.result }}
+
+
+
+
+
\ No newline at end of file
diff --git a/web_src/js/features/playground.js b/web_src/js/features/playground.js
new file mode 100644
index 0000000000..2de96754fc
--- /dev/null
+++ b/web_src/js/features/playground.js
@@ -0,0 +1,11 @@
+import {createApp} from 'vue'
+import Playground from '../components/Playground.vue'
+
+export function initPlayground() {
+ const el = document.getElementById('playground');
+ if (!el) return;
+
+ const View = createApp(Playground);
+ View.mount(el);
+}
+
diff --git a/web_src/js/index.js b/web_src/js/index.js
index bab1abfa36..0d0a3ad2ba 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -88,6 +88,8 @@ import {initRepositorySearch} from './features/repo-search.js';
import {initColorPickers} from './features/colorpicker.js';
import {initRepoMilestoneEditor} from './features/repo-milestone.js';
+import {initPlayground} from './features/playground.js';
+
// Init Gitea's Fomantic settings
initGiteaFomantic();
initDirAuto();
@@ -190,4 +192,6 @@ onDomReady(() => {
initPdfViewer();
initScopedAccessTokenCategories();
initColorPickers();
+
+ initPlayground();
});