Website2026-08-21
DJ Njace — a one-page portfolio concept built around scroll-controlled video
Independent demo, not commissioned client work. A site for a DJ that sells the atmosphere with a scrolling film, and closes the deal with three answers on the same page: if the date is available, how much it costs, and what's included. Without the 3D scene that was there before and without any plugin that should be maintained.
- Client
- Independent demo — no client
- Activity
- DJ for birthdays, celebrations, parties, weddings and proms
- Area
- Vranje and the south of Serbia, in the demo story of the whole of Serbia
- Type
- One-page scroll-film site with booking form
- Foreign
- 23 sitemap addresses, all return 200
- Technologies
- Next.js 16 · React 19 · Tailwind · nginx · systemd
- Status
- Live demo on my subdomain
- Technology
- Next.js 16 · React 19 · Tailwind · nginx · ffmpeg
Problem and context
What is DJ Njace and why does this site exist
DJ Njace is an independent demo rather than commissioned client work. It shows how a complete DJ or artist website could work, from the opening frame to a submitted inquiry. There is no active performer behind the name, and the site footer clearly labels all contact details, figures and locations as illustrative.
The demo story follows a DJ from Vranje who performs at birthdays, christenings, house parties, weddings, graduation celebrations and company events, traveling from southern Serbia to Leskovac, Niš, Belgrade, Kragujevac and Novi Sad. The fictional offer covers folk music, traditional urban songs, current regional hits, pop, dance, a club set and kolo when the event calls for it. A sound system, wireless microphone, LED lighting and backup equipment are included, so the host does not need to arrange extra production.
This positioning was chosen because it is difficult to communicate well. DJ sites often resemble club flyers: a dark background, logo, Instagram link and phone number. Someone organizing a family celebration or an eighteenth birthday usually has three immediate questions: is the date available, what does it cost, and is the equipment included? The entire demo is structured around those questions, while the visual atmosphere keeps the visitor engaged long enough to reach the answers.
What needed to be solved
The brief was narrow, with four firm requirements:
- Atmosphere before detail — a performer sells an experience. The opening screen needs to suggest a full dance floor before it lists services.
- The same atmosphere on a phone — most visitors arrive from Instagram on mobile devices, so a desktop-only effect would miss the primary audience.
- Visible starting prices — competitors often hide prices and force visitors to ask. A visible starting figure for each event type filters unsuitable inquiries and shortens the conversation.
- An inquiry that reaches its destination — the form needs real server-side handling, validation and bot protection, rather than a button that only displays a success message.
I also applied the same constraint used across my projects: no platform subscription, no fragile plugin dependency and no unnecessary media weight added for implementation convenience.
One scrolling page
The homepage is about nine thousand pixels long and is divided into six strips: introduction, counter, selection, parquet, lights and booking. The background is glued to the screen while the strip changes, so it doesn't look like the page is scrolling, but rather that the scene is moving.
On the left, on the desktop, there is a thin guide — a curve drawn through seven nodes, which is rendered along the scroll and lights the nodes as you go through the sections. On the phone, it turns into a real rail along the edge. The whole thing was written by hand, without an animation library, because the job is just one Catmull-Rom curve and one dashed line displacement.
The titles in the bars are real HTML, not text baked into the video. That sounds like a small thing until you remember that the alternative is a page that says nothing for browsers and screen readers. Here, each bar is read even without an image.
Around that one page are about fifteen additional addresses that cover what people actually type into the search — by type of celebration, by city, and by genre. Thus, DJ za slavu has its side with folk songs, house celebs and loudness that does not raise the neighborhood, instead of being one paragraph on the homepage. There are a total of 23 URLs in the sitemap and all return the correct answer.
Scrolling movie
Instead of an animation that spins on its own, the background is a video that the visitor moves through on his own. On the desktop, scrolling controls the video playhead: the playhead follows the page position and softly catches up with it, so the frame goes forward and backward in either direction.
The same trick doesn't work on the phone. Mobile browsers do not allow precise video rewinding without interaction, so the first solution was a self-playing loop. The owner of the demo refused, and rightly so — a video that starts on its own is exactly what is perceived as a nuisance on a phone. The replacement is a sequence of frames on the canvas: 84 images in WebP format, 2.8 megabytes in total, the first ten are loaded immediately, the rest are rendered in the background while the browser is idle, and painted only when the frame changes. The result preserves the same sense of rewinding, just without the video.
There are two exits under it. If animation reduction or data saving is on, a still image is displayed. If the video or sequence fails for any reason, the section automatically reverts to the gradient and the text remains readable. None of those paths leaves a blank page.
That this is not a theory, the behavior was verified on a real phone: the video element has no source and is paused, and the screen at three different scroll positions gives three different images, which are returned in the same order when returning. A little thing worth remembering — this site is demanding for tools that work in a browser window, because a continuous drawing loop keeps the page busy. A headless browser on the server remains suitable for automated checks.
How the movie was made
The film has not been shot and has not been purchased. It is made from a single image. First, a master shot of the club was generated in the brand palette, and on it the character behind the counter was changed with the order to keep everything else intact — so the composition remains identical. Camera movement was then added locally, through eight points per image, with smooth transitions and without a single cut, on the image magnified twice so that the movement is not stepped.
The reason for this procedure is banal: generating a real video costs credits, which were not enough at the time. It turns out that the limitation has an upside — a camera that moves one frame at a time never skips and never blinks, which is more important for scrolling than for regular video.
Coding is a separate story. The video being rewound must be composed of the keyframes themselves, otherwise at each jump it is decoded backwards to the nearest key and the picture is delayed. This movie has 192 frames and all 192 are key, at a resolution of 1440 pixels wide, and fits in 7.4 megabytes. That figure was reached through rehearsals: a dense computer frame with LED walls, confetti and a disco ball is compressed worse than a photorealistic recording, so the first versions were twice as difficult. The phone sequence was taken from the same film, at a lower resolution and with fewer frames.
What came before: the 3D scene that was dropped
The first version of this site had a real 3D scene in the viewer — a turntable, mixer, equalizer, stage and headphones, through which the camera scrolled. It looked impressive in description and bad in practice, and it's worth saying why, because it's a lesson that bears repeating.
- Stalls when entering a new object. The graphics card prepares materials only when something first becomes visible, so each new part of the scene brought a short pause. It's solved by warming up all materials before rendering and a curtain to hide it — but that's already a layer of code that exists just to cover the problem.
- The phone only saw the middle strip. The camera angle set for widescreen on an upright phone cuts the scene to a narrow strip. It's fixed by having the camera read the aspect ratio of each frame, but that's also an added complexity.
- Lighting without an environment map. The out-of-the-box ambient light library pulls a file from someone else's server, which our security policy doesn't allow, so the scene knew to stay black. The solution was manually placed light and lighter materials.
Every one of those things was fixed, and the site was working. But the sum was clear: for a site that needs to conclude the term, 3D was an expense without return. In the redesign, that entire layer was deleted — the 3D, animation, and soft scrolling libraries left the project and were not replaced. All that's left is the framework, React, and one helper package for the classes.
The film gives the same impression for a fraction of the price, works on every phone and doesn't waste graphics while the visitor reads the price list.
Price list that doesn't hide
The page DJ performance price list exists because search is full of queries that start with "how much does it cost" and almost no one answers them. Here, the starting price is written according to the type of celebration — birthday, celebration, party, wedding, graduation, company celebration — with a clear note that the final amount depends on the duration, location, number of guests and technical requirements. The amounts on the demo are illustrative, as is everything else on it.
Below the prices is a list of what is included in each, then a section on how the term is locked, then questions asked exactly as they are typed into the search, with short answers. Short answers are not a stylistic choice — it's a form that a search engine can extract and display as a separate answer.
Each service also carries structured information about the offer, which is generated from the same record from which the displayed price is printed. It's the easiest place for the site to mess with itself — the price changes on the page, and the old version remains in the structured data, so the browser shows the wrong amount for months.
Booking form and email that would go blank
The page Contact and booking is looking for what it is for the offer you really need: 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 there is a receiver on the server with a field check, a hidden field as a trap for automatons and a limit on the number of submissions per IP address per unit of time. The contact form is the easiest target for mass mailing and the most common place where a site turns into a spam channel.
Since this is a demo, the request receiver is currently just writing and not sending it to anyone — there's no point in creating a real message flow for a performer that doesn't exist. With a real contractor, the same sequence that we use everywhere follows: the inquiry is entered first, and only then is the email attempted, so that a canceled email does not mean a lost job.
One find from this project is worth more than anything else in this section. The site used addresses of the form booking@djnjace.svilenkovic.rs in the contact and privacy policy. They looked neat and were completely dead: the subdomain is not a postal domain, there is no record of receiving mail, so any mail sent to those addresses would silently bounce. If there was a real artist behind the demo, he would lose every request to delete data and every inquiry that gets past the form. The addresses were transferred to the right mailbox, and the rule has since been the same on every site: the contact address must exist on a domain that actually receives mail.
Technical performance, cache and deploy
The site is written in Next.js 16 with React 19 and Tailwind, runs as a persistent process on its own port, and is fronted by nginx that forwards traffic and holds the certificate. The service starts itself after the server is restarted and has its own log. No database is needed because the content of the demo is in the code.
The most expensive lesson here concerned caching. After one change, the project owner reported twice that the video on his phone was still playing automatically, even though the server was demonstrably serving the new version. The cause was an old media file retained in the phone’s cache, which cannot be deleted from our side. From that came two rules that are still valid. First: HTML is sent with no caching, and media files use their own rule with a one-day deadline, because otherwise the general rule would leave stale media cached for a month. Second: the new version of the movie gets a new file name, because renaming is the only reliable way to bypass someone else's cache.
Deploy has its own pitfalls and they are written down. Building the project is never done as an administrator — it is enough that one file in the working directory remains owned by the administrative user, so that the next build breaks the permissions, and since the script does not then restart the service, the site works on the old version for a while and only at the next restart enters a restart loop. That's why it's the same sequence every time: backup, delete old output, build under the right user, then restart.
Security headers are set in a full set and can be checked with a single request: force HTTPS with a long timeout, content policy, no nesting, and closed camera, microphone, and location permissions.
SEO, schema, and what's left open
The header carries five blocks of structured data — music artist, local entertainment business, person, site and FAQ. Service and location pages have their own, with service description and offer.
One bug was found just in time. In the header, there was a hard-written canonical title address, which was printed on every page next to the correct one. While the site was closed for crawling, it didn't bother anyone. If he had entered the search like that, the search engine would have merged all the subpages into the homepage and the fifteen carefully written landing pages would not have existed. It was removed, and now each page has exactly one canonical URL — its own.
The sitemap has 23 addresses and none of them leads to a redirection or an error, which is the most common silent omission after major changes. The cookie bar works on the consent standard, with the ability to refuse and remember choices; demo analytics is not included and the cookie policy says so instead of describing a measurement that doesn't exist.
Two things remain open and it is only fair that they stand here. The first: the demo was deliberately released into the index, to see how this structure behaves in a search for DJ-for-fame type queries — and at the same time there is a clear indication in the footer that it is a portfolio view. The two don't line up perfectly and that's a conscious decision, not an oversight. Second: in the structured data there is still a rating with the number of reviews from the demo set. On a real artist's site, it goes out the same day or is replaced with real reviews, because a fabricated rating in structured data is the fastest way to get a site out of the results.
Outcome
What this project demonstrates
A performer who would get a site like this gets one page that sells the feeling and answers the date, price and equipment in the same breath, a price list that shortens the correspondence, a form that actually receives the inquiry, and about fifteen pages that cover the type of celebration, city and genre. All this in your own code, with no monthly subscription and no add-ons.
The test was done from the server, with a windowless browser: front and key subpages, widescreen and phone, without a single error in the console and without horizontal overflow, with the behavior of the form and cookie bar checked. For a site of this type, it is the only valid way, because the film cannot be captured through an ordinary browser window.
What the demo replaces with the real deal is the content, not the code: real photos and videos instead of generated film, real numbers and mailboxes, real prices, real referrals. The structure remains the same because it's built to be repeatable — the same scheme works for the band, the host, the photographer, and anyone selling a term, not a product. More about how I work and what is in the portfolio can be found on the page about the artist, which is written on the demo exactly as it would be written for a real person.
FAQ
Frequently asked questions
Is DJ Njace a real artist?
No. It's our portfolio demo and it's labeled as such at the footer of the site. The contact, figures, referrals and locations shown are illustrative and cannot be scheduled. The site exists to show what a finished solution looks like for a performer.
Why a movie instead of a 3D scene in the browser?
Because a 3D version of this site existed and was kicked out. It caused crashes when entering new objects, only showed the middle part of the scene on the phone, and required a layer of code that exists only to cover it. The film gives the same impression, works on every phone and does not consume graphics while the visitor is reading the price list.
How does scroll-film work on a phone?
Not through video. Mobile browsers do not allow precise rewinding without interaction, so the phone draws a sequence of 84 images on the canvas, a total of about three megabytes. The first ten arrive immediately, the rest are rendered in the background. If animation reduction or data saving is on, a still image is displayed.
Can a site like this be made for a real artist?
It can, and that was the point of the demo. The content changes, not the structure: the right photos and recordings, the right number and inbox, the right prices and the right recommendations. The rating and numbers from the demo set are removed in doing so, as the fabricated data in the structured data carries a real penalty in the search.
Why are the prices visible when the competition hides them?
Because a visible starting price rejects the wrong inquiries before they happen and shortens the correspondence with the ones that stay. Additionally, the search is full of questions that start with how much does it cost, and the page with a clear answer to those has a huge advantage over the page that invites the message.
Does the demo form actually submit an inquiry?
No on the demo. The receiver receives and checks the request, but does not forward it to anyone, because there is no real contractor behind the site. On the client site, the sequence is always the same: the inquiry is entered first, and only then is the email attempted, so that a canceled message does not mean a lost job.
Next step
Have a project that needs to work better?
Send me your goal and current situation. I will reply with a concrete scope proposal.
Start a project