Balkan RP
a FiveM community site driven by a single video
A roleplay community site doesn't sell a service, it sells atmosphere: whoever opens it has to feel the city before reading a single rule. This is our subdomain and our project, with no client behind it. The whole page is one sixteen-second flight through a city, and seven chapters take turns over it as you scroll. No framework and not a single JavaScript library.
- Our own demo
- Serbia
- August 2026
- 13 min read
- Vanilla JS
- CSS
- Higgsfield
- ffmpeg
- nginx
- Customer
- None — our own demo, with no client
- Industry
- FiveM roleplay community (server invented for the showcase)
- Area
- Balkans, online
- Type
- Site driven by a scroll video, seven chapters
- Technologies
- Vanilla JS · CSS · AI video · ffmpeg · nginx
- Website
- rp.svilenkovic.rs
- Status
- Live since July 2026, deliberately not indexed

01 / 10
What Balkan RP is and whose site this is
Balkan RP is not client work. It is our subdomain and our project, built without a client, and the site itself says so: in the corner of the screen there is a badge that reads DEMO, and the terms of use state in their first sentence that no RP server by that name exists and that all the numbers, rules and organizations shown are invented for the showcase.
What is real is the audience. FiveM is a GTA V modification that allows private servers, and roleplay communities from the Balkans are among its bigger users. A server like that has its own Discord, its own rules, its own factions and its own donations, and almost always a website that is either a forum template or a single in-game screenshot with a button to Discord.
So the content on the site is written as if it were real: whitelist applications, an in-game economy, jobs, factions with a hierarchy, heists, custom maps and donations that don't break the balance. The chapters are called Start, About the Server, Character, Factions, Heists, Donations and Join, in exactly the order a prospective player asks questions: where am I, who am I, who am I with, what is there to do, how much does it cost, how do I get in.
Why build a demo for a job nobody ordered at all? Because it was a testing ground for a technique that is later used on real sites: an entire site driven by a single video instead of a 3D scene in the browser.

02 / 10
What needed solving
The audience for a site like this is the most demanding a site can get: gamers who look at in-game graphics every day. A page with a stock photo and three cards means nothing to them. The first impression has to be the city itself.
- Atmosphere before text — the visitor has to see movement through the city before reading a single rule.
- Runs on weak devices — part of the audience uses phones and computers that can barely run the game itself. The site must not be heavier than the game.
- No heavy 3D — the first version of this site was a Three.js scene where a single module weighed 934 KB. That is too much for one landing page, and it looked like a 3D scene, not like a city.
- Same content, several modes — the same page has to make sense when the visitor asks for reduced motion, is saving data or has no working scripts at all.
The decision was to drop the 3D and replace it with a film: one continuous flight through a city, shot so that scrolling can drive it. The camera moves forward without a single cut, from the waterfront and the pier, through golden hour over the center, past the towers and across a square with a fountain, to a neon boulevard at dusk. The chapters change over it, and the text color follows the time of day in the shot.
Technically, it trades one problem for another, but it is a good trade. A three-dimensional scene burns CPU every second the page is open; a video is downloaded once and then only decoded, and decoding is a job the device has dedicated hardware for. For content that is a predetermined camera path anyway, a film is cheaper on every device and more convincing to the eye.

03 / 10
The film is made in segments, not in one go
Sixteen seconds of continuous flight don't come out in one take. Image-to-video generators produce segments of a few seconds, so the film is assembled by chaining them.
The process was the same for every segment: first a single still frame is generated at full size, motion is made from it, and then a still is pulled from the last frame of that segment (ffmpeg -sseof) and becomes the first frame of the next one. The seams don't show because at the end of every segment the camera moves into something (the glass of a tower, the shadow of an overpass), so the join happens at a moment when the frame is dark anyway. It isn't a trick applied to the footage but a condition written into the generation prompt.
Two details from the encoding are worth writing down. First: before the final pass, the footage goes through light denoising (hqdn3d), which makes the file about a fifth lighter, and the texture lost that way is brought back by a CSS grain layer over the video that costs nothing measurable. Second: the film is encoded so that every frame stands on its own (-g 1), because in this mode it isn't played but scrubbed, and seeking to a frame that isn't a keyframe means decoding back to the nearest keyframe.
An unpleasant surprise came from the generator's billing. The price check before generating reported one amount of credits, but more than double was actually deducted per segment. The rule we have followed since: after the first generation, check the account balance, and only then run the batch. A preflight check is not a promise.
04 / 10
Seven chapters over one film
The page has no sections stacked one below another. It has a single scene pinned to the top of the screen and a track 1050vh tall on wide screens (800vh on phones) that you scroll through. The ratio is deliberate: one second of film corresponds to roughly two-thirds of a screen of scrolling, slow enough to see the shot and fast enough not to get boring.
The chapters are absolutely positioned along the track, at zero, then at 14, 28, 42, 57 and 71 percent. The last one, with the instructions for joining, isn't placed at a percentage but pinned to the bottom of the track. That isn't a style choice but a fix: while it was also placed at a percentage, on shorter screens it slipped below the end of the film, so a visitor would leave the scene before ever seeing the call to action. The last chapter hooks onto the bottom, not onto a percentage, a small detail you don't notice until it breaks.
Each chapter has its own mood (golden, dusk, neon, night), and the accent color of the eyebrows, chips and numbers follows the film's own arc from day to night. When text sits over video, the only thing that keeps it readable is that its color changes along with the shot beneath it; a fixed palette would disappear into the background halfway through the film.
On the side there is a seven-dot navigation that shows where you are in the story, and a thin progress bar at the top. Both are derived from the same number, the current position in the film, so they can't drift apart, which is the most common bug on pages that calculate progress and navigation separately.
05 / 10
Three modes, one and the same content
The same site behaves in three ways, and which one applies is decided on load.
- Scrub — on a device with a mouse, the video itself loads and its time follows the scroll position, with light smoothing so the motion isn't jittery and a threshold below which nothing changes, so no work is spent on movement smaller than one frame.
- Frame sequence — on touch devices, frames are drawn on a canvas instead of playing the video: 161 WebP images, about 5 MB in total, loaded progressively.
- Poster — if data saving is on or the visitor asks for reduced motion, the film doesn't load at all. The page turns into an ordinary scrolling page with the poster at the top and the chapters below.
The third mode isn't an alibi but a health check for the whole idea. The chapter text is real text in the document, the chapters are real sections with their own headings and IDs, and the only part that depends on the script is the cards appearing on scroll, built so that without the script the cards show immediately instead of staying invisible. Without JavaScript the site loses the film, not the content.
Loading the sequence is split: the first twelve frames right away, the rest eight at a time whenever the browser has nothing better to do. That is also where this site's most expensive bug came from. The call that schedules work for idle time takes its second argument as an object with a timeout, not as a number, and a number had been written. The result was an error right on load that killed the whole script, and only on phones, because that code path never runs on a wide screen. On a computer the site looked perfectly fine while on a phone it was dead. Hence the habit of opening every code path that exists for only one type of device on that actual device, not in a simulation.
06 / 10
Technical implementation
The whole site is three files to maintain: one HTML file, one CSS file of about ten kilobytes and one JavaScript file of five. No framework, no packages, no build step. What you open in the editor is exactly what gets served, so making a change doesn't depend on whether the tool the site was built with is still maintained.
The film files carry a version in their names (hero-scrub-a1.mp4, frames-a1/, poster-a1.jpg), and the server caches them for a year as immutable. That is the only way long caching and future changes can coexist: when the film is replaced, the name changes, not the content at the same address. Overwriting the video in place is invisible: neither the author nor the visitor sees the new encode, so you conclude the fix doesn't work, even though it does.
The film is delivered in two sizes: a full one for wide screens, about 20 MB, and a narrower one for small screens, about 9.5 MB. The server also answers range requests, so seeking doesn't wait for the whole file. The HTML is deliberately not cached, because it is the only file allowed to change at the same address.
In front of everything sits nginx with a full set of security headers and a content security policy, with access denied to hidden files and backups, and with a permanent redirect from HTTP to HTTPS. The certificate is issued by Let’s Encrypt, as for the rest of the server.
One local detail you only remember once it hurts: this site's home page is locked at the file system level. Until it is unlocked it can't be overwritten, not by unpacking an archive and not by a plain copy, and the error message doesn't say why. The convention is useful until someone forgets it, so it is written down with the project itself.
07 / 10
Speed and what the film really costs
The first thing you see isn't the video but the poster: a single frame of about 150 KB that loads with the highest priority and covers the whole screen. The video sits on top of it and appears only once it has enough data to display, with a short crossfade. So at no point does the visitor look at a black screen, and the element that makes up the page's first render is an image, not the film.
The video itself isn't touched until it is decided which mode applies. In the document it has no source and an instruction not to preload anything; the script assigns the source, and only when it has chosen scrub. On a phone the full version is never even requested, and in poster mode neither version is.
The grain layer and the vignette over the film are one SVG noise and one gradient, neither of them needing a single pixel from the network, and both are painted once, not per frame. That is what gives the feel of a film instead of game footage, and it costs nothing measurable. The headline on the first screen is plain text with a gradient, not an image, so it stays sharp at any resolution and can be read aloud.
The fonts load from Google's service, and that is the only thing on the page that comes from outside. For a real client site they would be self-hosted; here they were left as they are, and that is stated in the privacy policy instead of being kept quiet.
08 / 10
What is still open
The engine that drives this film seeks the video: it writes a new time into it on every scroll frame. It is the usual recipe and it works here, but later, on another site from the same family, measurements showed it isn't the best one. Every write to the video's time is a separate seek: the browser flushes the decoding pipeline, jumps to the requested frame and shows it, and writes that arrive while the seek is in progress get swallowed. In a fast transition only some of the frames are shown instead of all of them, and the eye reads it as a slideshow, not a film.
The better solution is for the video, during the transition, to play, with its playback rate steered by how far the scroll has moved and how far there is still to go. Seeking is kept only for jumping backward and for landing at the end. This also takes a new encode, because the “every frame is a keyframe” recipe pushes the stream into a format no graphics card decodes in hardware, so everything falls on the CPU, exactly where we were trying to save the most.
The same goes for choosing the mode. Here a phone is recognized by the fact that it is a touch device, so it gets the image sequence. It is more correct to ask the device what it can play, not how it is touched: today's phones handle real video well, and the image sequence gives them fewer frames than they would get from the film. The sequence still makes sense as a fallback when the video fails, not as the main route.
Both are known, measured and written down, and both require a new encode of the film and a new version tag in the file names. For a site that isn't client work and isn't indexed, that is an item waiting its turn, but it is written down here, because a finding you keep quiet about isn't a finding.
An extension of the film itself is also planned: a third segment, a night finale with the neon boulevard and a rise toward the city skyline. The source material and the encoding script are ready on the server; all that is left is generating it and lengthening the track so the new stretch of film has room to play through.
09 / 10
An honest label, legal texts and indexing
A demo that looks like a real site can easily turn into a lie. That is why the label is visible on every page instead of being hidden in the footer: a small badge reading DEMO is fixed in the corner of the screen, on the home page and on the legal pages alike, and doesn't move when you scroll.
The site is deliberately kept out of search. The document carries a noindex directive, and robots.txt blocks crawling of the entire site. Another consequence is that it has no structured data: schema that presents a fictional organization as real is exactly what you shouldn't do, so there is none at all. For a real client that would be the first layer added; here it is the first one left out.
Privacy Policy and the terms weren't copied from anyone. They describe what actually happens: that there are no forms, that no personal data is collected, that the server keeps standard access logs and how long it keeps them, and that the fonts are pulled from a third-party service. The first sentence of the terms says the server doesn't exist and that the join, sign-up and donation buttons don't trigger any process.
Among the small things that are rarely done: at the top of the page there is a skip link that goes straight to the join chapter, the chapters follow a correct heading hierarchy, and the video is taken out of the keyboard tab order and marked as decorative, because there is nothing to do with it from the keyboard. The server connect address copies with one click, with a short confirmation instead of a silent action. The footer shows the year and the author, without a single trace of the tools used to build the site.
10 / 10
What this project shows
For someone running a roleplay community, the point in short is this: a site that looks like a trailer for the server doesn't require the visitor to have a powerful machine, and its content is plain text edited in one file. The server name, slot count, rules and sign-up steps are sentences, not images, so they can be changed in a minute and don't need to be redrawn when something on the server changes.
For anyone else, this is a demonstration of a technique that has nothing to do with gaming. The same engine can drive a presentation of a house, a mobile bar or a product: wherever there is one continuous movement that tells a story by itself, a film is cheaper and more convincing than a 3D scene in the browser, and measurably lighter on a weaker device.
What carried over from this project is the method, not the code: the film is made in segments and chained through the last frame; the mode is chosen by what the device can do, not by what kind of device it is; file names carry a version, because otherwise a long cache is a trap for whoever makes the change; the last chapter hooks onto the bottom of the track; and the page has to stay readable even when the film doesn't show at all.
The site has been live since July 2026 at rp.svilenkovic.rs and stays there as a demo, with no client and no server behind it. If it ever gets one, the content is already written and the page is waiting for it.
On a phone
The same site, in your hand.
Most visitors come from phones, so the layout, buttons and images are arranged for the small screen first.

Questions
What clients ask.
Does Balkan RP actually exist as a server?
No. The site is our demo on our own subdomain, built without a client, and it says so on the site itself: a DEMO badge sits in the corner of the screen, and the first sentence of the terms of use says the server is fictional. All the numbers, rules and factions on the page are there to show what real content would look like, not for anyone to sign up anywhere based on them.
Does a site like this open on a low-end phone?
It does, because the film isn't delivered the same way to everyone. Touch devices get a sequence of lighter images instead of the full video, and if data saving is on or reduced motion is requested, the film doesn't load at all and the page becomes an ordinary scrolling page with a poster at the top. In all three cases, the first thing you see is a single image of about 150 KB.
What does a visitor see with JavaScript turned off?
All of the text. The chapters are real sections with their own headings, not something a script injects later. All that drops away is the film and the cards appearing on scroll. In that case the cards are visible right away instead of staying hidden, which is the most common mistake on pages with scroll reveals.
Can the film be replaced later without reworking the site?
Yes, but it has to get a new name. The film files carry a version in their names, and the server caches them for a year as immutable, so a visitor would never see a file overwritten under the same name. Replacing it means a new file with a new version tag and two changed lines of code.
Why video and not a 3D scene in the browser?
Because the camera path is fixed in advance and there is nothing to change interactively. The first version of this site was in fact a 3D scene, and a single one of its modules weighed close to a megabyte, with constant CPU use while the page was open. A video is downloaded once and then only decoded, it looks more convincing and it doesn't punish a weaker machine.
Can a site like this be found in search?
For a real client, yes, and then it gets per-page titles, descriptions, a sitemap and structured data. This one is deliberately kept out of the index: it carries a noindex directive and a robots.txt that blocks crawling, because a fictional server has no business in search results.
Related projects
From the same field.

Klasičan Gejming
DJ Njace