DJ Njace
one-page DJ website with a film that moves as you scroll
Our own demo, not client work. A DJ website that sells the atmosphere with a film that moves as you scroll and closes the deal with three answers on the same page: is the date free, how much does it cost and what is included. No 3D scene, which used to be here, and not a single plugin that would need maintaining.
- Our own demo
- Vranje
- August 2026
- 12 min read
- Next.js 16
- React 19
- Tailwind
- nginx
- ffmpeg
- Customer
- Our own demo — no client
- Industry
- DJ for birthdays, slava celebrations, parties, weddings and proms
- Area
- Vranje and southern Serbia; in the demo's story, all of Serbia
- Type
- One-page scroll-film website with a booking form
- Pages
- 23 URLs in the sitemap, all returning 200
- Technologies
- Next.js 16 · React 19 · Tailwind · nginx · systemd
- Website
- djnjace.svilenkovic.rs
- Status
- Live demo on our subdomain

01 / 11
What DJ Njace is and why this site exists
DJ Njace is not a client. It is our own demo, a site built to show what a complete web presence for a DJ and performer looks like, from the first frame to a filled-in form. There is no active act behind the name, and the site's own footer says so: the contact details, numbers and locations shown are illustrative.
The demo's story is deliberately ordinary, not premium. A DJ from Vranje who plays birthdays, slava celebrations, house parties, weddings, proms and company events, based in southern Serbia and traveling as far as Leskovac, Niš, Belgrade, Kragujevac and Novi Sad. The repertoire: Serbian folk and old city songs, domestic pop hits, pop and dance, a club set for the peak of the night, and the kolo circle dance when the crowd asks for it. The sound system, wireless microphone, LED lighting and backup gear come with the booking, so the host does not have to rent anything extra.
That positioning was chosen because it is the hardest one for a website. DJ sites usually look like a club flyer: a black page, a logo, Instagram and a phone number. Someone looking for a DJ for a slava or an eighteenth birthday is not buying festival aesthetics. They have three questions — are you free on that date, how much does it cost and do you bring your own gear. The whole demo is built around those three questions, and the atmosphere is there to keep the visitor on the page until they get to them.

02 / 11
What needed solving
The brief we set ourselves was narrow, but it had four firm points:
- Atmosphere before text — a performer's work is sold on feeling. The first screen has to convey what a packed dance floor looks like, not list services.
- The same atmosphere on a phone — most visitors come from a phone, via Instagram. An effect that works only on desktop is an effect that does not work.
- Price visible right away — competitors hide it and push people to send a message. A visible starting price for each type of celebration filters out the wrong inquiries right away and shortens the back-and-forth.
- An inquiry that lands somewhere — the form needs real server-side handling, with validation and bot protection, not a button that just prints a success message.
On top of that came the condition we set ourselves on every project: no platform, no plugins that fall apart with the next update, and not a single megabyte that loads just because it was easier than thinking.

03 / 11
One page that scrolls
The home page is about nine thousand pixels long and opens with a film in six segments: intro, decks, selection, dance floor, lights and booking. The background stays pinned to the screen while the segments change, so it does not feel like scrolling down a page but like the scene itself is moving.
Below the film, on screens wider than 1500 pixels, a thin guide line winds down the page — a curve threaded through seven nodes that draws itself as you scroll and lights up the nodes as you pass through the sections. On narrower screens and on phones it is not there. The whole thing is written by hand, with no animation library, because the job is just one Catmull-Rom curve and one dash offset.
The headings in the segments are real HTML, not text baked into the video. That sounds like a detail until you remember that the alternative is a page that says nothing to search engines or screen readers. Here every segment can be read even without the image.
Around that one page sit about fifteen additional URLs that cover what people actually type into search — by type of celebration, by city and by genre. That way, DJ for a slava gets its own page, with folk music, slava celebrations at home and a volume that will not wake the neighbors, instead of being one paragraph on the home page. In total there are 23 URLs in the sitemap, and all of them return a valid response.
04 / 11
A film that moves as you scroll
Instead of an animation that plays on its own, the background is a film the visitor moves through. On desktop, scrolling moves the playhead in the video: a loop tracks the page position and eases toward it, so the frame moves forward and backward exactly as much as you scroll.
On a phone the same trick does not work. Mobile browsers do not allow precise video seeking without user interaction, so the first solution was a loop that played on its own. The owner of the demo rejected it, and rightly so — a video that starts by itself is exactly what feels like a nuisance on a phone. The replacement is a frame sequence on a canvas: 84 WebP images, 2.8 megabytes in total, fetched only once the visitor starts scrolling and only around the current frame, and drawn only when the frame actually changes. The result is the same scrubbing feel, just without the video.
Underneath that there are two fallbacks. If reduced motion or data saver is turned on, a still image is shown. If the video or the sequence fails for any reason, the section falls back to a gradient on its own and the text stays readable. None of those paths leaves a blank page.
To make sure this is not just theory, it was checked on a real phone: the video element has no source and stays paused, while the canvas shows three different images at three different scroll positions, and they come back in the same order when you scroll back. A detail worth remembering — this site cannot be screenshotted with tools that work through a browser window, because the continuous drawing loop keeps the page busy. The reliable check is a server running a headless browser.
05 / 11
How the film was made
The film was not shot and not bought. It was made from a single image. First a master shot of the club was generated in the brand palette, then the figure at the decks was changed, with the instruction to leave everything else untouched — so the composition stays identical. The camera movement was then added locally, through eight points across the image, with soft transitions and not a single cut, on an image upscaled two times so the motion would not look jagged.
The reason for this approach is mundane: generating real video costs credits, and at the time there were not enough of them. The constraint turned out to have an upside — a camera moving across a single frame never skips and never flickers, which matters more for scroll scrubbing than for ordinary video.
Encoding is a story of its own. A video that gets scrubbed has to consist of keyframes only, because otherwise every jump has to decode back to the nearest keyframe and the image lags. This film has 192 frames, all 192 of them keyframes, at 1440 pixels wide, and it fits in 7.4 megabytes. Getting to that number took trial and error: a dense computer-generated frame with LED walls, confetti and a disco ball compresses worse than photorealistic footage, so the first versions were twice as heavy. The phone sequence was extracted from the same film, at a lower resolution and with fewer frames.
06 / 11
What came before: the 3D scene that was dropped
The first version of this site had a real 3D scene in the browser — a turntable, a mixer, an equalizer, a stage and headphones, which the camera moved through as you scrolled. It sounded impressive on paper and worked badly in practice, and it is worth saying why, because this lesson keeps coming back.
- Stuttering whenever a new object came into view. The graphics card prepares materials only when something first becomes visible, so every new part of the scene caused a brief hitch. It was solved by warming up all the materials before rendering and a curtain to hide that — but that is already a layer of code that exists only to cover up a problem.
- The phone saw only the middle strip. A camera angle set up for a wide screen cuts the scene down to a narrow band on a phone held upright. It was fixed by having the camera read the aspect ratio of every frame, but that, too, is added complexity.
- Lighting without an environment map. The off-the-shelf library for ambient lighting pulls a file from a third-party server, which the site's security policy does not allow, so the scene would sometimes stay black. The fix was hand-placed lighting and brighter materials.
Each of those things was fixed, and the site worked. But the sum was clear: for a site whose job is to book a date, 3D was a cost with no return. In the redesign that entire layer was deleted — the libraries for 3D, animation and smooth scrolling left the project and were not replaced with anything. All that remained was the framework, React and one small helper package for classes.
The film gives the same impression for a fraction of the cost, works on every phone and does not tax the graphics while the visitor reads the price list.
07 / 11
A price list that does not hide
The DJ Performance Price List page exists because search is full of queries that start with “how much does it cost”, and almost nobody answers them. Here the starting price is listed by type of celebration — birthday, slava, party, wedding, prom, company event — with a clear note that the final amount depends on duration, location, number of guests and technical requirements. The amounts on the demo are illustrative, like everything else on it.
Below the prices comes a list of what each one includes, then a section on how a date gets booked, then questions phrased exactly the way people type them into search, with short answers. The short answers are not a stylistic choice — that is the format a search engine can pull out and show as a featured answer.
Each service also carries structured offer data, generated from the same record the displayed price is printed from. That is the easiest place for a site to contradict itself — the price changes on the page, the old version stays in the structured data, and the search engine shows the wrong amount for months.
08 / 11
The booking form and an email that would have gone nowhere
The Contact and Booking page asks for what a quote really requires: name, phone, email, date, type of celebration, city and estimated number of guests. The message is optional; consent to data processing is not.
Behind the form is a server-side handler with field validation, a hidden honeypot field to trap bots and a limit on submissions per IP address per unit of time. A contact form is the easiest target for mass submissions and the most common place where a site turns into a spam channel.
Since this is a demo, the handler currently only records the inquiry and does not send it to anyone — there is no point in building a real message flow for a performer who does not exist. For a real performer, the same order we use everywhere applies: the inquiry is saved first and only then is the email attempted, so a failed email does not mean a lost job.
One finding from this project is worth more than everything else in this section. In its contact details and privacy policy, the site listed addresses like booking@djnjace.svilenkovic.rs. They looked tidy and were completely dead: the subdomain is not a mail domain and has no record for receiving mail, so every message sent to those addresses would have quietly bounced. Had there been a real performer behind the demo, they would have lost every data deletion request and every inquiry that arrived outside the form. The addresses were moved to a real inbox, and since then the rule has been the same on every site: the contact address must exist on a domain that actually receives mail.
09 / 11
Technical build, cache and deployment
The site is written in Next.js 16 with React 19 and Tailwind, runs as a persistent process on its own port, and sits behind nginx, which proxies the traffic and holds the certificate. The service comes back up on its own after a server restart and has its own log. No database, because the demo content lives in the code.
The most expensive lesson here was about caching. After one change, the owner reported twice that the video on the phone was still playing by itself, even though the server was demonstrably serving the new version. The cause was an old bundle stuck in the phone's cache, which cannot be cleared from our side. Two rules came out of that, and they still apply. First: HTML is sent with caching disabled, and media get their own rule with a one-day lifetime, because otherwise the general rule would lock them in for a month. Second: a new version of the film gets a new file name, because renaming is the only reliable way to get around someone else's cache.
Deployment has its own traps, and they are written down. The project is never built as root — it only takes one file in the working directory being left owned by root for the next build to fail on permissions, and since the script then does not restart the service, the site keeps running the old version for a while and only falls into an error loop at the next restart. That is why the order is the same every time: backup, delete the old output, build as the right user, then restart.
The full set of security headers is in place and can be checked with a single request: enforced HTTPS with a long max-age, a content security policy, a ban on embedding in other sites' pages, and camera, microphone and location permissions switched off.
10 / 11
SEO, schema and what remains open
The home page carries five blocks of structured data — music performer, local entertainment business, person, website and FAQ. The service and location pages have their own, with a service description and an offer.
One bug was caught just in time. The head contained a hard-coded canonical URL for the home page, which was printed on every page alongside the correct one. While the site was closed to indexing, this bothered no one. Had it gone into search like that, the search engine would have folded all the subpages into the home page, and fifteen carefully written landing pages would effectively not exist. It was removed, and now every page has exactly one canonical URL — its own.
The sitemap has 23 URLs, and none of them leads to a redirect or an error, which is the most common silent oversight after bigger changes. The cookie banner follows the consent standard, with an option to decline and a remembered choice, and Google Analytics loads only after the visitor accepts analytics cookies.
Two things remained open, and it is only fair to list them here. First: the demo was deliberately let into the index, to see how a structure like this performs in search for queries such as DJ for a slava — while the footer clearly states that this is a portfolio showcase. Those two do not sit together perfectly, and that is a conscious decision, not an oversight. The second has since been closed: the rating with a review count from the demo data was removed from the structured data. On a real performer's site that comes out the same day or is replaced with real reviews, because a made-up rating in structured data is the fastest way for a page to drop out of the results.
11 / 11
What this project shows
A performer who got a site like this would get one page that sells the feeling and, in the same breath, answers the questions about date, price and gear; a price list that shortens the back-and-forth; a form that actually receives inquiries; and about fifteen pages covering type of celebration, city and genre. All of it in custom code, with no monthly subscription and no plugins.
Testing was done from the server with a headless browser: the home page and key subpages, wide screen and phone, without a single console error and without horizontal overflow, with the behavior of the form and the cookie banner checked. For a site of this type that is the only reliable method, because the film cannot be captured through an ordinary browser window.
What gets replaced on the demo for a real job is the content, not the code: real photos and videos instead of the generated film, a real phone number and inbox, real prices, real testimonials. The structure stays the same, because it was built to be repeatable — the same pattern works for a band, an MC, a photographer and anyone who sells a date rather than a product. More about how the performer works and what is in his portfolio is on the page about the performer, which on the demo is written exactly the way it would be written for a real person.
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.
Is DJ Njace a real performer?
No. It is our portfolio demo, and it is labeled as such in the site footer. The contact details, numbers, testimonials and locations shown are illustrative, and no gig can actually be booked. The site exists to show what a finished solution for a performer looks like.
Why a film instead of a 3D scene in the browser?
Because a 3D version of this site existed and was dropped. It stuttered whenever new objects came into view, showed only the middle of the scene on a phone, and needed a layer of code that existed only to cover that up. The film gives the same impression, works on every phone and does not tax the graphics while the visitor reads the price list.
How does the scroll-film work on a phone?
Not through video. Mobile browsers do not allow precise seeking without user interaction, so on a phone a sequence of 84 images is drawn on a canvas, about three megabytes in total. The images load only once the visitor starts scrolling, and only the ones around the current frame. If reduced motion or data saver is turned on, a still image is shown instead.
Can a site like this be built for a real performer?
Yes, and that was the point of the demo. The content changes, not the structure: real photos and videos, a real phone number and inbox, real prices and real testimonials. The rating and numbers from the demo data are removed in the process, because made-up data breaks search engine rules and can cost a site its eligibility for rich results.
Why are prices visible when competitors hide them?
Because a visible starting price filters out the wrong inquiries before they happen and shortens the back-and-forth with those who remain. On top of that, search is full of questions that start with “how much does it cost”, and a page that answers them clearly has a big advantage over a page that just asks you to send a message.
Does the form on the demo actually send an inquiry?
Not on the demo. The handler receives and validates the inquiry but does not forward it to anyone, because there is no real performer behind the site. On a client site the order is always the same: the inquiry is saved first and only then is the email attempted, so a failed message does not mean a lost job.
Related projects
From the same field.

Trubači Stars
Trubači Stars — Belgrade