Early prototype — you can use it as a start point for a portfolio. Expect rough edges, missing features, and breaking changes. Feedback and contributions are welcome.
A content-first terminal portfolio template. Add your text and files, change a few settings, then deploy. You do not need to write code to get a complete site.
Visitors boot into a BIOS screen. Then they explore your work with shell commands: ls, cat, wget, and more.
- How the project is organized
- Quick start
- Content checklist
- Your content
- Site settings
- Writing tips
- Preview and publish
- What visitors can do
- File reference
- Resources used
- License
- Contributing
TTYfolio keeps what you say apart from how the terminal works. Each layer has one job. Change only the layer you need.
| Layer | You edit | Purpose |
|---|---|---|
| Content | src/data/vfs/*.md |
Bio, projects, contact, certs — what visitors read |
| Assets | assets/documents/ |
Photo, CV, social preview image |
| Identity | src/data/config.js |
Domain, shell username, status bar |
| Intro | src/data/boot.js |
Boot screen copy and timing (optional) |
| Engine | src/ TypeScript |
Terminal UI, shell, rendering (template code — you rarely change it) |
| Build | scripts/ |
Turns content into a site you can deploy (template code) |
Content and assets are enough to publish. Identity takes five fields. The rest is optional polish.
The build writes the files in src/data/generated/. Never edit them by hand.
You need Node.js 18 or newer.
npm install
npm run devOpen http://localhost:3000. Save a file and the page reloads.
If another program uses port 3000:
fuser -k 3000/tcp 2>/dev/null; lsof -ti :3000 | xargs -r kill -9
npm run devComplete each step. The steps do not depend on each other.
- Replace the text in
src/data/vfs/about.txt.md - Replace the text in
src/data/vfs/contact.txt.md - Replace the text in
src/data/vfs/projects.txt.md - Update
src/data/vfs/certs.txt.md(optional) - Add
assets/documents/profile.jpg(or.png),cv.pdf, andsite.jpg(or.png) - Set your domain and username in
src/data/config.js - Run
npm run buildand uploaddist/
This gives you a complete portfolio.
All copy lives in src/data/vfs/. Each .md file becomes one file in the virtual home folder.
Edit the body below the front matter. The front matter is the --- block at the top. It sets the filename and the shell alias. Leave the front matter alone unless you add a new file.
File: src/data/vfs/about.txt.md
Write who you are and what you do. Use **bold** for headings. To link to another file, use markdown. For example: [see certs.txt](certs.txt).
You can also add these fields to the front matter. Search engines and the browser tab title use them:
displayName: Your Name
jobTitle: Developer
location: EarthVisitors open this file with cat about.txt or the [ABOUT] menu button.
File: src/data/vfs/projects.txt.md
List your projects. Use this pattern:
# Projects
> My App [live demo](https://example.com)
Short description of what it does.
[GitHub](https://github.com/you/my-app)Repeat the block for each project. The terminal shows lines that start with > as titles.
File: src/data/vfs/contact.txt.md
Edit the list items directly. The links live here, not in config.js:
- **Email:** [you@example.com](mailto:you@example.com)
- **GitHub:** [github.com/you](https://github.com/you)
- **CV:** [DOWNLOAD CV](cv.pdf)The terminal makes these links clickable. cv.pdf starts a download. A .txt file opens with cat.
File: src/data/vfs/certs.txt.md
A simple bullet list. Link to it from your about page if you want.
Replace the files in assets/documents/:
| File | Used for |
|---|---|
profile.jpg or profile.png |
The terminal shows it when a visitor runs cat profile_pic.jpg. The build writes an optimized .jpg. |
cv.pdf |
A visitor downloads it with wget cv.pdf or the contact link. |
site.jpg or site.png |
The preview image for a shared link on social media. The build writes an optimized .jpg. |
The vfs entries (profile_pic.jpg.md and cv.pdf.md) already point to these paths. Replace the files, not the config.
File: src/data/config.js
Shell identity only. No bio and no contact links:
| Setting | Example | Purpose |
|---|---|---|
url |
https://yoursite.com |
Canonical URL for SEO and the sitemap |
hostname |
yoursite.com |
The terminal shows it in the shell prompt and the boot text |
handle |
guest |
Username in the prompt (guest@yoursite.com) |
statusBar.value |
AVAILABLE FOR HIRE |
Short status line at the bottom |
statusBar.menu |
[ABOUT], and more |
Quick-action buttons |
profileRedaction |
(optional) | Censor band on cat profile_pic.jpg — read the next section |
To change the boot screen text and timing, edit src/data/boot.js.
Add profileRedaction to src/data/config.js to draw a black censor band across part of your profile photo. The band appears when a visitor runs cat profile_pic.jpg. Each view picks a random band position and width inside your ranges. Each line drifts a little for a glitch effect.
Redaction applies only to the profile image in normal scanline mode. The terminal does not change other images.
The enhance pipe — add | enhance after any cat of an image file. The terminal then shows the image at full resolution, with no censor band and no retro pixelation. The scanline reveal runs slower.
cat profile_pic.jpg # pixelated terminal look + censor band (if you configure one)
cat profile_pic.jpg | enhance # full image, no censor bandenhance is not a separate command. It works only as a pipe after cat on an image file. It works on any image in the vfs, not only the profile photo.
All row values are pixels in the source image. Check the dimensions in an image editor. Band position and width use fractions from 0 to 1 across the image width.
| Field | Meaning |
|---|---|
lineStart |
Top row of the censored band (y pixel) |
lineEnd |
Bottom row of the censored band (y pixel) |
visibleMin / visibleMax |
Minimum and maximum width of the black bar, as a fraction of the image width |
startMin / startMax |
Minimum and maximum left edge of the bar, as a fraction of the image width |
lineJitter |
Optional drift per row (default 0.015, a lower value makes a tighter band) |
Example — censor a band across the middle third of a portrait about 300 px tall:
profileRedaction: {
lineStart: 100,
lineEnd: 200,
visibleMin: 0.2,
visibleMax: 0.4,
startMin: 0.15,
startMax: 0.45,
lineJitter: 0.01,
},To show the photo without a censor band, remove profileRedaction.
- Write bold text with
**double asterisks**. - Write a heading as
# Title, or as**Title**on its own line. - Write a web or email link as
[label](url). Write a link to another terminal file as[label](file.txt). - Write a contact line as
- **Label:** valueor- **Label:** [text](url).
Run cat help.txt in the terminal for a short command list.
npm run dev # local preview, reloads on save
npm run build # production files in dist/Upload the dist/ folder to any static host. GitHub Pages, Netlify, and Cloudflare Pages all work.
Before you publish, put your real domain in assets/robots.txt if you use that file.
| Command | What it does |
|---|---|
ls |
Lists the files in the home folder |
ls -h |
Also lists hidden files, for example .bashrc |
cat about.txt |
Reads a text file |
wget cv.pdf |
Downloads your CV |
help |
Shows the available commands |
clear |
Clears the screen |
The aliases about, contact, and projects run the matching cat command.
| Path | Layer | Role |
|---|---|---|
src/data/vfs/*.md |
Content | All terminal file text |
assets/documents/ |
Assets | Photo, CV, social preview |
src/data/config.js |
Identity | Domain, username, status bar |
src/data/boot.js |
Intro | Boot screen |
src/ (TypeScript) |
Engine | Terminal app — read CONTRIBUTING.md |
scripts/ |
Build | Turns content into a static site |
dist/ |
Output | Deploy this folder |
- Phaser — terminal rendering and input
- VT323 and Press Start 2P — terminal fonts, from Fontsource
- gray-matter — reads the content file headers at build time
- esbuild — bundles the app
- BetterCRT shader — included at
assets/shaders/bettercrt.glsl
PolyForm Noncommercial License 1.0.0 — free for personal and non-commercial use. Commercial use needs separate permission.
This license covers the template code. Dependencies such as Phaser and the fonts keep their own licenses. Read THIRD_PARTY_NOTICES.md. The build writes the full Phaser MIT text to dist/assets/vendor/phaser.LICENSE.md.
Read CONTRIBUTING.md for the architecture, the coding standards, and the pull request rules.






