Most of what users upload after sunset looks like the photo on the left. Low light photo enhancement, also called low light image enhancement, used to mean an editor or a batch job. Here it’s one segment in the image URL, and the original file never changes. The photo on the right is the same upload, served through enhance=preset:fix_dark.
To brighten dark user photos automatically, add enhance=preset:fix_dark (after any resize) to the image URL. The stored original never changes.
Key takeaways
enhance=preset:fix_darkgoes in front of the handle in the delivery URL, and the stored original stays as it was uploaded.- Dark photos are dark in different ways. On the photos below,
fix_darklifted a night street,fix_tintandautobrightened a restaurant interior, andfireworksmade a snowy dusk scene darker. resizegoes in front ofenhance, so the correction runs on the size you serve. On a 26 MP camera original, that order is also what lets the request finish.fix_noisereads the ISO and the camera model from EXIF, so keep metadata on the stored file.- Each distinct URL is processed once and then served from the CDN cache. A 1x and 2x pair is two processed images per photo, not one per view.
What one URL segment does to a dark phone photo
A stored photo has a CDN URL ending in its handle. Corrections are path segments placed in front of the handle:
https://cdn.filestackcontent.com/HANDLE
https://cdn.filestackcontent.com/enhance=preset:fix_dark/HANDLE
https://cdn.filestackcontent.com/resize=width:1200/enhance=preset:fix_dark/HANDLE
HANDLE is the last segment of the file’s URL. enhance sits beside resize, crop and the other tasks of the image editing API, and the tasks run left to right. Bare enhance applies the auto preset. Image Enhancement runs on Start, Grow and Scale. With application security on, enhance still answers an unsigned request, so it works on the plain URLs your app already serves.
Put resize first. The correction then runs on the pixels you deliver. On a 26 MP camera original, enhance with nothing in front of it times out with 503 first byte timeout, and resize=width:1200/enhance=preset:fix_dark on the same file answers in about five seconds.
A preset name the task doesn’t know returns a 400 that names it:
validation error: invalid value format fix_darkk for parameter preset required by enhance task (invalid value fix_darkk)
Four ways to brighten user photos
| Option | What it does | The catch |
|---|---|---|
CSS filter: brightness() on the img |
Lightens the pixels on screen | The same curve on every photo. It lifts noise with the shadows, washes out highlights, and the file is unchanged everywhere else it’s shown |
sharp or ImageMagick on your server (modulate, gamma, normalise) |
Real pixel changes you control | You choose the rules per photo, run the CPU for every size, and store or cache the results yourself |
| An editor in the upload flow | The user fixes their own photo | Many users skip the step |
enhance in the delivery URL |
A per-photo correction chosen by preset, cached at the CDN | The preset choice is yours, which the next section covers |
If your team already runs an image pipeline on its own servers, adding a brightness step there is a reasonable choice. The last row is smart image processing at delivery, with nothing to run.
Pick a preset for night streets, interiors and lights
“Dark” covers several problems, and each preset is aimed at one of them. The figures below show one photo per scene, each at resize=width:1200, with the mean brightness of the 8-bit greyscale image under each version. The numbers describe these photos only.
A night street. fix_dark is documented for extreme underexposure and a dark subject against bright light. On this taxi, auto takes the mean from 45.8 to 60.2, and fix_dark takes it to 83.9. The road, the taxi’s bodywork and the cars behind it come up out of the shadow, and the shop signs and street lights stay bright.
A restaurant interior. fix_tint is documented for colour casts, including the yellow of tungsten bulbs. This bowl of ramen sits on a warm wooden counter. fix_tint brightens it from 73.9 to 94.9 and keeps the warm tones. auto goes further, to 111.4. For a food photo, where the warmth belongs to the scene, auto is the one to compare first.
Lights in a dark frame. fireworks is documented for a dark night sky with excess colour. This cabin in the snow at dusk has lit windows but no sky full of colour, and fireworks takes it from 49.2 down to 37.1, with a purple shift on the snow on the roof. fix_dark lifts the same frame to 94.8 and shows the snow and the birch trunks. The preset follows the problem in the photo, not the time of day it was taken.
Your app usually knows more about a photo than its pixels do: which form it came from, which part of the product shows it. Use that to pick the preset per upload route:
const PRESET_BY_KIND = {
venue_interior: 'auto',
street: 'fix_dark',
event: 'fix_dark',
};
function enhanceSegment(kind) {
const preset = PRESET_BY_KIND[kind];
return preset ? `enhance=preset:${preset}` : 'enhance';
}
There are nine preset values in all, and the full enhance preset table describes each one.
Keep the camera data so grain removal works
fix_noise decides how much grain to remove from the ISO and the camera make and model in the file’s EXIF data. Photos straight off a phone or a camera carry them. They disappear when something re-encodes the file before upload, such as a canvas resize in the browser, or a server step that strips metadata before storing.
An upload through filestack-js or the picker with no resize options stores the file byte for byte, EXIF included. If your app resizes in the browser or strips metadata for privacy, run fix_noise on a copy that kept the camera fields, or pick a different preset for that route.
Show the brightened photo right after upload
This page uploads with the picker and shows each photo brightened, with a toggle back to the original. The stored file never changes, so the original is one URL away.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Night photos</title>
<style>
figure { display: inline-block; margin: 0 1rem 1rem 0; }
img { width: 400px; height: auto; display: block; }
</style>
</head>
<body>
<button id="upload">Upload photos</button>
<div id="gallery"></div>
<script src="https://static.filestackapi.com/filestack-js/4.x.x/filestack.min.js"></script>
<script>
const client = filestack.init('YOUR_API_KEY');
function enhancedUrl(handle, size, preset = 'fix_dark') {
return `https://cdn.filestackcontent.com/resize=width:${size},height:${size},fit:max/enhance=preset:${preset}/${handle}`;
}
function originalUrl(handle, size) {
return `https://cdn.filestackcontent.com/resize=width:${size},height:${size},fit:max/${handle}`;
}
function render(handle) {
const img = document.createElement('img');
img.src = enhancedUrl(handle, 800);
img.srcset = `${enhancedUrl(handle, 800)} 1x, ${enhancedUrl(handle, 1600)} 2x`;
img.alt = 'Uploaded photo, brightened';
const toggle = document.createElement('button');
toggle.textContent = 'Show original';
toggle.onclick = () => {
const showingOriginal = toggle.textContent === 'Show enhanced';
const url = showingOriginal ? enhancedUrl : originalUrl;
img.src = url(handle, 800);
img.srcset = `${url(handle, 800)} 1x, ${url(handle, 1600)} 2x`;
toggle.textContent = showingOriginal ? 'Show original' : 'Show enhanced';
};
const figure = document.createElement('figure');
figure.append(img, toggle);
document.getElementById('gallery').append(figure);
}
document.getElementById('upload').onclick = () => {
client.picker({
accept: ['image/*'],
maxFiles: 10,
onUploadDone: (result) => result.filesUploaded.forEach((file) => render(file.handle)),
}).open();
};
</script>
</body>
</html>
Replace YOUR_API_KEY with the key from the Developer Portal and serve the page from a domain your application allows. If application security is on, the picker upload needs a signed policy, built as the security policy reference describes. The enhance URLs don’t.
fit:max inside a square box is there for portrait photos. Cameras and phones often store a portrait photo as landscape pixels with an EXIF orientation tag. width alone counts the stored pixels, so resize=width:1200 on one of those gives a photo 800 wide on screen. A square box with fit:max caps the longer side whichever way the photo was stored, and browsers apply the tag, so the photo shows upright.
What each brightened photo costs
The enhanced result is cached at the CDN. The first request for a URL is processed, and every request after it is served from cache, about a month of freshness at a time. The response carries Cache-Control: public with a max-age of roughly 30 days, and a repeat request comes back as a cache hit.
Each distinct URL is its own result. enhancedUrl(handle, 800) and enhancedUrl(handle, 1600) are two processed images, so the page above uses two per photo however many people view it. The original toggle uses plain resize URLs with no enhance in them.
Start, Grow and Scale include 1,000 enhanced images a month, then $0.04 an image.
For a feed that shows one photo at many sizes, correct it once and store the result. Adding store to the end of the chain saves the corrected image as a new file and returns its handle:
https://cdn.filestackcontent.com/resize=width:1600/enhance=preset:fix_dark/store/HANDLE
Then resize that stored copy for each size, with no enhance in those URLs.
When to leave a photo alone
A preset aimed at a problem the photo doesn’t have still changes it. This mountain town was shot in full sun. fix_dark takes it from 121.6 to 153.4, which lightens the sky and the house walls further than the scene needs.
So pick the preset per upload route, not one setting for every photo, and keep the toggle. Some users want their photo exactly as they took it.
Put one preset on one route and compare the two URLs on your own feed. A photo that’s well exposed but too small is a different problem, and the image upscaling parameters cover that one.
Frequently asked questions
How do I brighten dark user photos automatically?
Add an enhance segment in front of the file handle in the delivery URL, for example resize=width:1200/enhance=preset:fix_dark/HANDLE. The correction runs when the image is served and the stored original is not changed.
Which enhance preset works best for night photos?
It depends on the problem in the photo. On the test photos, fix_dark brightened a night street most, fix_tint and auto brightened a warm restaurant interior, and fireworks made a snowy dusk scene darker. Pick the preset per upload route.
Does low light enhancement change the original file?
No. The enhancement runs in the delivery URL, so the stored original stays as it was uploaded and is always one URL away.
How much does low light enhancement cost?
The Start, Grow and Scale plans include 1,000 enhanced images a month, then $0.04 an image. Each distinct URL is processed once and then served from the CDN cache.
Joshua is a web developer with over 4 years of experience building responsive, high-performance websites and web applications. Currently working as an AI Automation Specialist, he combines modern web development with automation to create efficient, scalable digital solutions. He shares practical insights on WordPress, web development, and emerging technologies.
Read More →


