Arcweave Blog

How to Create a Visual Novel Style in Arcweave's Play Mode

Written by Giannis Georgiou | November 20, 2025 at 8:54 PM

Transform your Arcweave project into a professional-looking visual novel with just a few CSS tweaks

Visual novels are one of the most popular storytelling formats in interactive fiction. With their full-screen backgrounds, character sprites, and dialogue boxes, they create an immersive reading experience that draws players into your story. The good news? You can achieve this polished look in Arcweave's Play Mode using custom CSS.

In this tutorial, we'll show you how to customize your Play Mode interface to look like a professional visual novel—no coding experience required!

By the end of this guide, you'll know how to:

  • Set full-screen background images for each scene
  • Position character sprites at the bottom of the screen
  • Create a stylish dialogue box with scrollable text
  • Place choice buttons above your dialogue
  • Style specific words and paragraphs for emphasis
  • Make everything responsive for mobile devices

Before we start

Ingredients

To follow this tutorial, you will need:

  • An Arcweave project with some elements, connections, and components
  • Image assets uploaded to your project (backgrounds and character sprites)

Preparation

Before diving into CSS, structure your project like this:

  • Use element covers for background images
  • Attach components (with cover images) to represent characters
  • Write your dialogue in the element content
  • Use connection labels for player choices

Now, let's get started!

Accessing the Style Editor

First, let's open the CSS editor where all the magic happens:

  1. Open your project and click the Play button (▶️) in the top menu
  2. In Play Mode, click the Style Editor icon (palette icon) in the top bar
  3. You'll see a code editor panel open on the right side

The Style Editor shows you the current CSS and lets you write custom styles that will apply to your entire Play Mode.

The 3 parts of the visual novel

A visual novel interface has three main visual parts that we'll customize:

  1. Full-screen backgrounds - The scenic backdrop for each scene
  2. Character sprites - Your characters positioned at the bottom
  3. Dialogue box - Where your text appears with scrolling for longer content

Let's tackle them one by one:

1. Full-screen backgrounds

By default, element covers appear as small images. Let's make them fill the entire screen:

/* Full-screen background container */
.prototype__media {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}


/* Make the image cover the screen */
.prototype__media img.single {
width: 100%;
height: 100%;
object-fit: cover;
}

 

What this does:

  • position: absolute lets us place the image anywhere
  • width: 100% and height: 100% make it fill the screen
  • object-fit: cover ensures the image fills the space without distortion
  • z-index: 1 puts it in the background, behind characters and dialogue

2. Character sprites

Characters should appear at the bottom of the screen, standing above the dialogue box:

/* Character container */
.prototype__components {
position: absolute;
bottom: 350px;
left: 0;
right: 0;
height: 60vh;
display: flex;
align-items: flex-end;
z-index: 2;
padding: 0 5%;
}

/* Character sprite styling */
.prototype__components .comp {
height: 100%;
background: none;
object-fit: contain;
object-position: bottom;
filter: drop-shadow(0 0 20px rgba(0, 0, 0, 0.5));
}

 

What this does:

  • bottom: 350px positions characters above the dialogue box
  • height: 60vh makes characters take up 60% of screen height
  • align-items: flex-end aligns characters to the bottom
  • object-position: bottom keeps feet anchored at the bottom
  • filter: drop-shadow() adds a subtle shadow for depth

💡 Customization tip: Adjust bottom: 350px to change how high characters appear. Try 400px for more space or 300px for less.

3. Dialogue box with scrollable text

The dialogue box is where your text appears. This is important: we need to make sure long text can scroll!

/* Dialogue box container */
.prototype__body {
position: absolute;
bottom: 50px;
left: 50px;
right: 50px;
height: 300px;
background: linear-gradient(135deg,
rgba(30, 41, 59, 0.85) 0%,
rgba(15, 23, 42, 0.98) 100%);
border: 2px solid #475569;
border-radius: 15px;
z-index: 3;
padding: 25px 60px;
backdrop-filter: blur(10px);
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
display: flex;
}

/* Text container with scroll functionality */
.prototype__text {
overflow: auto;
width: 100%;
scrollbar-width: thin;
scrollbar-color: #475569 transparent;
}

/* Custom scrollbar for Chrome/Safari */
.prototype__text::-webkit-scrollbar {
width: 6px;
}

.prototype__text::-webkit-scrollbar-track {
background: transparent;
}

.prototype__text::-webkit-scrollbar-thumb {
background: #475569;
border-radius: 3px;
}

/* Text styling inside the box */
.prototype__text .editor .ProseMirror {
color: #ffffff;
font-size: 30px;
line-height: 1.7;
font-family: 'Segoe UI', 'Noto Sans', sans-serif;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.6);
}

 

What this does:

  • bottom: 50px positions it near the screen bottom
  • background: linear-gradient() creates a semi-transparent gradient
  • backdrop-filter: blur(10px) blurs the background behind the box
  • border-radius: 15px rounds the corners
  • overflow: auto - This is crucial! It enables scrolling when text is long
  • scrollbar-width: thin and ::-webkit-scrollbar style the scrollbar to match your theme
  • text-shadow makes text more readable

⚠️ Important: Without the overflow: auto property, long dialogue will be cut off! This ensures players can always read everything.

Styling choice buttons

Player choices should appear above the dialogue box:

/* Options container */
.prototype__options {
position: absolute;
bottom: 360px;
left: 50%;
transform: translateX(-50%);
display: block;
z-index: 4;
width: 90%;
max-width: 800px;
}

/* Individual choice button */
.prototype__option {
background: linear-gradient(135deg,
rgba(51, 65, 85, 0.95) 0%,
rgba(30, 41, 59, 0.98) 100%);
border: 2px solid #60a5fa;
border-radius: 50px;
padding: 20px 35px;
margin: 0 0 30px;
color: #ffffff;
font-size: 24px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 20px rgba(59, 130, 246, 0.3);
}

/* Hover effect */
.prototype__option:hover {
transform: translateY(-4px) scale(1.05);
border-color: #a78bfa;
box-shadow: 0 8px 30px rgba(99, 102, 241, 0.4);
}

 

What this does:

  • bottom: 360px places buttons just above the dialogue box
  • transform: translateX(-50%) centers them horizontally
  • border-radius: 50px creates pill-shaped buttons
  • transition: all 0.3s smoothly animates hover effects
  • :hover makes buttons lift up when you mouse over them

Using CSS variables for easy customization

Here's a pro technique: define your colors once at the top, then reuse them throughout:

:root {
/* Dialogue box theme */
--vn-dialogue-bg: linear-gradient(135deg,
rgba(30, 41, 59, 0.85) 0%,
rgba(15, 23, 42, 0.98) 100%);
--vn-dialogue-border: #475569;
--vn-dialogue-text: #ffffff;

/* Button theme */
--vn-option-bg: linear-gradient(135deg,
rgba(51, 65, 85, 0.95) 0%,
rgba(30, 41, 59, 0.98) 100%);
--vn-option-border: #60a5fa;
--vn-option-text: #ffffff;

/* Text sizes */
--vn-font-size-dialogue: 30px;
--vn-font-size-option: 24px;
}

 

Then use them like this:

.prototype__body {
background: var(--vn-dialogue-bg);
border-color: var(--vn-dialogue-border);
}

 

Why this is useful: Change one value at the top and it updates everywhere! Perfect for trying different color schemes.

Mobile responsiveness

Don't forget about mobile players! Add these media queries:

@media (max-width: 1000px) {
/* Adjust character positioning */
.prototype__components {
bottom: 150px;
height: 50vh;
}

/* Smaller dialogue box */
.prototype__body {
bottom: 20px;
left: 20px;
right: 20px;
height: 150px;
padding: 15px 30px;
}

/* Smaller text */
.prototype__text .editor .ProseMirror {
font-size: 20px;
}

/* Adjust options */
.prototype__options {
bottom: 180px;
}

.prototype__option {
font-size: 18px;
padding: 12px 30px;
}
}

Advanced technique: styling specific words and paragraphs

Here's where things get really interesting! You might want to emphasize certain words—like a character's name in a different color, important clues in bold and red, or sound effects in a stylized font.

The Challenge: Arcweave doesn't let you inject custom HTML classes directly into your text.

The Solution: We can "hijack" the formatting tools that Arcweave already provides!

How it works

When you format text in Arcweave's editor (italic, bold, underline), it generates standard HTML tags:

  • Italic creates <em> tags
  • Bold creates <strong> tags
  • Underline creates <u> tags

We can target these tags with CSS and override their default appearance. The key is to use the element ID to make sure we only affect specific elements, not every italic word in your entire story.

Finding element IDs

There are two easy ways to find an element's ID:

Method 1: Using the debugger (recommended)

  1. Enter Play Mode
  2. Click on Debug in the top menu
  3. Navigate to the element you want to style
  4. Look at the Current Element section - you'll see the ID listed there
  5. Copy the ID (it looks like el-cb35db92-3c07-41bf-9c45-580ad6b891b4)

Method 2: From the Editor

  1. In your project, right-click the element you want to style
  2. Select Properties
  3. The element ID is shown in the “Details” tab
  4. Copy the ID

Step-by-step process

1. Format text in the editor

Go to your element and edit it:

  • Highlight the word or phrase you want to style
  • Apply a format: Italic, Bold, or Underline
  • This creates the HTML tag we'll target with CSS

2. Write the CSS

Now in the Style Editor, target that specific formatted text:

/* Target italic text in a specific element and make it red */
#el-your-element-id ditor .editor-content em {
color: red;
font-style: normal; /* Remove the default italic effect */
}

Practical examples

Example 1: Character names in color

Make character names appear in their signature color:

In Arcweave: Format "Alice" with italic CSS:

#el-your-element-id .prototype__text .editor .editor-content em {
color: #60a5fa; /* Blue for Alice */
font-style: normal;
font-weight: bold;
}

Example 2: Important clues

Highlight clues that players should remember:

In Arcweave: Format clue words with bold CSS:

#el-your-element-id .prototype__text .editor .editor-content strong {
color: #fbbf24; /* Gold color */
font-weight: 900;
text-shadow: 0 0 10px rgba(251, 191, 36, 0.5); /* Glow effect */
}

Example 3: Sound effects

Create stylized sound effects:

In Arcweave: Format "crash" with underline CSS:

#el-your-element-id .prototype__text .editor .editor-content u {
text-decoration: none; /* Remove underline */
color: #ef4444;
font-family: 'Impact', sans-serif;
font-size: 1.2em;
font-style: italic;
letter-spacing: 2px;
}

Example 4: Targeting specific paragraphs

You can also style entire paragraphs using nth-child:

/* Style the first paragraph differently (maybe it's narration) */
#el-your-element-id .prototype__text .editor .ProseMirror p:first-child {
color: #a78bfa;
font-style: italic;
font-size: 0.9em;
}

/* Style the second paragraph (maybe it's a character speaking) */
#el-your-element-id .prototype__text .editor .ProseMirror p:nth-child(2) {
color: #60a5fa;
font-weight: bold;
}

Creative uses

Here are some creative ways to use this technique:

1. Dialogue with different character voices:

/* Character 1 - uses italic */
#el-your-element-id em {
color: #60a5fa;
font-style: normal;
font-weight: 600;
}

/* Character 2 - uses bold */
#el-your-element-id strong {
color: #f472b6;
font-weight: 700;
}

/* Narrator - uses underline */
#el-your-element-id u {
text-decoration: none;
color: #a78bfa;
font-style: italic;
}

 

2. Thoughts vs. spoken dialogue:

/* Inner thoughts in italic */
#el-your-element-id em {
color: #94a3b8;
font-style: italic;
opacity: 0.8;
}

 

3. Branching indicators:

/* Highlight choices that lead to different paths */
#el-your-element-id strong {
color: #fbbf24;
font-weight: bold;
text-decoration: underline;
}

Important warnings

⚠️ Always include the Element ID! If you forget it, you'll accidentally style EVERY italic/bold/underline word in your entire project:

/* ❌ DON'T DO THIS - affects everything */
em {
color: red;
}

/* ✅ DO THIS - affects only one element */
#el-your-element-id em {
color: red;
}

 

⚠️ Limitations: You can only use three "channels" (italic, bold, underline) per element. If you need more variety, create different elements or use paragraph targeting.

⚠️ Testing: Always test in Play Mode to make sure your styles look right!

Complete code template

Here's the full CSS ready to copy and paste.

/* ========== FULL-SCREEN BACKGROUND ========== */
.prototype__media {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}

.prototype__media img.single {
width: 100%;
height: 100%;
object-fit: cover;
}

/* ========== CHARACTER SPRITES ========== */
.prototype__components {
position: absolute;
bottom: 350px;
left: 0;
right: 0;
height: 60vh;
display: flex;
align-items: flex-end;
z-index: 2;
padding: 0 5%;
}

.prototype__components .comp {
height: 100%;
background: none;
object-fit: contain;
object-position: bottom;
filter: drop-shadow(0 0 20px rgba(0, 0, 0, 0.5));
}

/* ========== DIALOGUE BOX ========== */
.prototype__body {
position: absolute;
bottom: 50px;
left: 50px;
right: 50px;
height: 300px;
background: linear-gradient(135deg,
rgba(30, 41, 59, 0.85) 0%,
rgba(15, 23, 42, 0.98) 100%);
border: 2px solid #475569;
border-radius: 15px;
z-index: 3;
padding: 25px 60px;
backdrop-filter: blur(10px);
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
display: flex;
}

/* Text container with scrolling */
.prototype__text {
overflow: auto;
width: 100%;
scrollbar-width: thin;
scrollbar-color: #475569 transparent;
}

/* Custom scrollbar for Chrome/Safari */
.prototype__text::-webkit-scrollbar {
width: 6px;
}

.prototype__text::-webkit-scrollbar-track {
background: transparent;
}

.prototype__text::-webkit-scrollbar-thumb {
background: #475569;
border-radius: 3px;
}

/* Text styling */
.prototype__text .editor .ProseMirror {
color: #ffffff;
font-size: 30px;
line-height: 1.7;
font-family: 'Segoe UI', 'Noto Sans', sans-serif;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.6);
}

/* ========== CHOICE BUTTONS ========== */
.prototype__options {
position: absolute;
bottom: 360px;
left: 50%;
transform: translateX(-50%);
display: block;
z-index: 4;
width: 90%;
max-width: 800px;
}

.prototype__option {
background: linear-gradient(135deg,
rgba(51, 65, 85, 0.95) 0%,
rgba(30, 41, 59, 0.98) 100%);
border: 2px solid #60a5fa;
border-radius: 50px;
padding: 20px 35px;
margin: 0 0 30px;
color: #ffffff;
font-size: 24px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 20px rgba(59, 130, 246, 0.3);
}

.prototype__option:hover {
transform: translateY(-4px) scale(1.05);
border-color: #a78bfa;
box-shadow: 0 8px 30px rgba(99, 102, 241, 0.4);
}

/* ========== STYLING SPECIFIC WORDS (EXAMPLE) ========== */
/* Replace with your actual element ID */
#el-your-element-id .prototype__text .editor .editor-content em {
color: #60a5fa;
font-style: normal;
font-weight: bold;
}

/* ========== MOBILE RESPONSIVE ========== */
@media (max-width: 1000px) {
.prototype__components {
bottom: 150px;
height: 50vh;
}

.prototype__body {
bottom: 20px;
left: 20px;
right: 20px;
height: 150px;
padding: 15px 30px;
}

.prototype__text .editor .ProseMirror {
font-size: 20px;
}

.prototype__options {
bottom: 180px;
}

.prototype__option {
font-size: 18px;
padding: 12px 30px;
}
}

Quick customization guide

Want to tweak the look? Here are the most common adjustments:

What to Change Where to Look Example Values
Character height bottom: 350px in .prototype__components 300px - 500px
Character size height: 60vh in .prototype__components 40vh - 80vh
Dialogue box position bottom: 50px in .prototype__body 30px - 100px
Dialogue box height height: 300px in .prototype__body 200px - 400px
Text size font-size: 30px in .ProseMirror 24px - 36px
Scrollbar color scrollbar-color in .prototype__text Any hex color
Button colors border: 2px solid #60a5fa in .prototype__option Any hex color
Button shape border-radius: 50px in .prototype__option 0px (square) - 50px (pill)

Pro tips & tricks

Tip 1: Color themes

Create different moods by changing the gradient colors:

Dark & Mysterious:

background: linear-gradient(135deg,
rgba(15, 15, 25, 0.9) 0%,
rgba(5, 5, 15, 0.95) 100%);

 

Light & Airy:

background: linear-gradient(135deg,
rgba(240, 240, 250, 0.85) 0%,
rgba(220, 220, 240, 0.95) 100%);

Tip 2: Custom fonts

Want a different font? Add this at the top:

@import url('<https://fonts.googleapis.com/css2?family=Your+Font+Name&display=swap>');

.prototype__text .editor .ProseMirror {
font-family: 'Your Font Name', sans-serif;
}

Tip 3: Multiple characters

If you have multiple character components attached to an element, they'll automatically arrange side-by-side thanks to display: flex in .prototype__components.

Tip 4: Scrollbar styling

Match your scrollbar to your theme by changing the color:

.prototype__text {
scrollbar-color: #your-color transparent;
}

.prototype__text::-webkit-scrollbar-thumb {
background: #your-color;
}

Tip 5: Testing text overflow

To test if your scrolling works, write a long element with lots of text. The scrollbar should appear automatically when content exceeds the dialogue box height.

Troubleshooting common issues

Characters are cut off at the top

Solution: Increase height: 60vh to 70vh or 80vh

Dialogue box covers the characters

Solution: Increase bottom: 350px in .prototype__components

Text is hard to read

Solution: Increase the opacity in your background gradient (change 0.85 to 0.95)

Buttons are too close to dialogue box

Solution: Increase bottom: 360px in .prototype__options

Mobile layout looks wrong

Solution: Check your media query values and adjust the breakpoint (max-width: 1000px)

Long text gets cut off instead of scrolling

Solution: Make sure you have overflow: auto on .prototype__text

Can't see the scrollbar

Solution: Check that scrollbar-width: thin and the webkit scrollbar styles are present

My styled words affect other elements

Solution: Make sure you're including the specific element ID: #el-your-id em { } not just em { }

What's next?

Now that you have a beautiful visual novel interface, here are some ideas to take it further:

  1. Add animations - Try transition and transform properties for fade-ins
  2. Custom buttons for specific choices - Use element IDs to style individual options
  3. Day/night themes - Create different CSS themes and switch between them
  4. Sound effects - Combine your CSS with Arcweave's audio features
  5. Character portraits - Try different positioning for character components
  6. Text effects - Experiment with glowing text, shadows, and animations
  7. Create a name tag system - Use paragraph targeting to style character names
  8. Animated scrollbar - Add smooth scrolling animations

Conclusion

Congratulations! You've just transformed your Arcweave project into a professional-looking visual novel. The best part? All of this styling happens in Play Mode, so you can iterate quickly without touching your project structure.

You've learned:

  • ✅ How to create full-screen backgrounds
  • ✅ How to position character sprites
  • ✅ How to design a beautiful dialogue box with scrolling text
  • ✅ How to style choice buttons
  • ✅ How to customize specific words and paragraphs
  • ✅ How to make everything mobile-responsive
  • ✅ How to find element IDs easily

Remember: CSS is all about experimentation. Don't be afraid to adjust values, try different colors, or completely redesign elements. The Style Editor updates in real-time, so you can see your changes immediately.

Want to share your creation? Use Arcweave's Share feature to make your project public and let others experience your visual novel!

Need more help? Join the Arcweave Discord community where you can share your CSS creations and get feedback from other creators.

Happy storytelling! ✨

Have you created something cool with these CSS techniques? We'd love to see it! Share your projects with us on Discord or tag us on social media.