Welcome to a tutorial on how to create a simple responsive PHP video gallery. Once upon a time in the stone age of the Internet, we have to struggle with all kinds of funky 3rd party video plugins to even play a single video. Fast forward to today, things are really easy.
Creating a simple video gallery in PHP is as easy as:
- Get a list of video files from the folder –
$vids = glob("GALLERY/*.{webm,mp4,ogg}", GLOB_BRACE);
- Output the HTML video tags –
foreach ($vids as $v) { echo "<video controls src='". rawurlencode(basename($v)) ."'></video>"; }
Yep, let us build a video gallery based on that – Read on!
ⓘ I have included a zip file with all the example source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.
TLDR – QUICK SLIDES
Fullscreen Mode – Click Here
TABLE OF CONTENTS
DOWNLOAD & NOTES
Firstly, here is the download link to the example code as promised.
QUICK NOTES
- Put all your video files into the
gallery
folder (no sample videos are provided in the zip file). - Launch
1a-gallery.php
for the “simple version”, and2a-caption-gallery.php
for the “gallery with captions”.
SCREENSHOT
EXAMPLE CODE DOWNLOAD
Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
SIMPLE PHP VIDEO GALLERY
All right, let us now get into more details about the PHP video gallery.
PART 1) VIDEO GALLERY PAGE
<!-- (A) CLOSE FULLSCREEN VIDEO -->
<div id="vClose" onclick="vplay.toggle(false)">X</div>
<!-- (B) VIDEO GALLERY -->
<div class="gallery"><?php
// (B1) GET ALL VIDEO FILES FROM THE GALLERY FOLDER
$dir = __DIR__ . DIRECTORY_SEPARATOR . "gallery" . DIRECTORY_SEPARATOR;
$vid = glob("$dir*.{webm,mp4,ogg}", GLOB_BRACE);
// (B2) OUTPUT ALL VIDEOS
if (count($vid) > 0) { foreach ($vid as $v) {
printf("<video src='gallery/%s'></video>", rawurlencode(basename($vid)));
}}
?></div>
<div id="vClose">
This “close” button will only show when a video is in fullscreen.- As in the introduction – Get all video files in the
gallery
folder, and output them into<video>
tags.
PART 2) CSS COSMETICS
/* (A) GALLERY WRAPPER */
/* (A1) BIG SCREENS - 3 IMAGES PER ROW */
.gallery {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 10px;
max-width: 1200px;
margin: 0 auto; /* horizontal center */
}
/* (A2) SMALL SCREENS - 2 IMAGES PER ROW */
@media screen and (max-width: 768px) {
.gallery { grid-template-columns: repeat(2, 1fr); }
}
/* (B) GALLERY VIDEOS */
/* (B1) THUMBNAIL VIDEO */
.gallery video {
width: 100%;
height: 200px;
object-fit: cover; /* fill | contain | cover | scale-down */
cursor: pointer;
}
/* (B2) FULLSCREEN VIDEO */
.gallery video.full {
position: fixed;
top: 0; left: 0; z-index: 999;
width: 100vw; height: 100vh;
background: #000;
object-fit: scale-down;
}
/* (C) EXIT FULLSCREEN */
#vClose {
position: fixed; display: none;
top: 0; right: 0; z-index: 9999;
font-size: 20px; font-weight: 700;
padding: 10px 15px;
color: #fff;
background: #741414;
cursor: pointer;
}
#vClose.show { display: block; }
There’s quite a bit of CSS, but keep calm and let’s walk through the important mechanics:
- (A1)
display: grid
andgrid-template-columns: repeat(3, 1fr)
pretty much does the “3 videos per row” layout magic. - (A2) On small screens, we change the layout to 2 videos per row.
- (B1)
width: 100%
andheight: 200px
will automatically resize the videos, but the videos will be “out of proportion”. We useobject-fit
to change the resize behavior, change this and see for yourself; Pick one that you like. - (B2) On clicking a thumbnail video, we set it to fullscreen. Pretty much 100% viewport width and height, and a fixed position.
- (C) The “close fullscreen video” button, place at the top right-hand corner.
PART 3) JAVASCRIPT
var vplay = {
// (A) INIT - CLICK VIDEO TO GO FULL SCREEN
init : () => { for (let v of document.querySelectorAll(".gallery video")) {
v.onclick = () => {
if (!v.classList.contains("full")) { vplay.toggle(v); }
};
}},
// (B) TOGGLE FULLSCREEN
toggle : e => {
// (B1) TOGGLE CLOSE BUTTON
document.getElementById("vClose").classList.toggle("show");
// (B2) TOGGLE VIDEO
let v = e===false ? document.querySelector(".gallery .full") : e ;
v.classList.toggle("full");
v.controls = e===false ? false : true ;
if (e===false) { v.pause(); }
}
};
window.onload = vplay.init;
A bit of Javascript to drive the “click to fullscreen video”.
- On window load,
init()
will attach “click to toggle fullscreen” on all gallery videos. - To toggle the fullscreen video. Basically, add/remove the
full
CSS class on the selected video.
EXTRA BITS & LINKS
That’s all for this project, and here is a small section on some extras and links that may be useful to you.
EXTRA) FILE NAME AS VIDEO CAPTION
// (B2) OUTPUT VIDEOS
if (count($vid) > 0) { foreach ($vid as $v) {
$file = basename($v);
$caption = substr($file, 0, strrpos($file, "."));
printf("<div class='vWrap'>
<video src='gallery/%s'></video>
<div class='vCaption'>%s</div>
</div>", rawurlencode($file), $caption);
}}
There is no database, but we can still use the file name as the caption.
EXTRA) SORTING THE VIDEOS
usort($vid, function ($file1, $file2) {
return filemtime($file2) <=> filemtime($file1);
});
usort($vid, function ($file1, $file2) {
return filemtime($file1) <=> filemtime($file2);
});
sort($vid); // ascending
rsort($vid); // descending
EXTRA) MULTIPLE CATEGORIES
<h1>CATEGORY A</h1>
<div class="gallery"><?php
$vid = glob(FOLDER A);
foreach ($vid as $v) { printf("<video ...>"); }
?></div>
<h1>CATEGORY B</h1>
<div class="gallery"><?php
$vid = glob(FOLDER B);
foreach ($vid as $v) { printf("<video ...>"); }
?></div>
Same old “get the list of files and output HTML”, just keep your video files in different folders.
EXTRA) READ ANOTHER FOLDER
$vid = array_merge(
$vid, glob("ANOTHER-FOLDER*.{webm,mp4,ogg}", GLOB_BRACE)
);
This is an alternative to reading multiple folders, just use array_merge()
to combine the results. But of course, this is only good as a “quick fix”, this is not good if you have a dozen folders.
COMPATIBILITY CHECKS
- Arrow Function – CanIUse
- CSS Grid Layout – CanIUse
This simple gallery will work on all modern browsers.
LINKS & REFERENCES
- HTML5 Video – Wikipedia
- HTML Video Tag – MDN
- CSS Grid – MDN
- Fullscreen API – MDN
TUTORIAL VIDEO
INFOGRAPHIC CHEAT SHEET

THE END
Thank you for reading, and we have come to the end of this guide. I hope that it has helped you with your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!
I posted a question a few minutes ago and I’m not seeing it to reply to it, but I found the answer. I was trying to get the URL in the address bar updated when a video was clicked on and I did so by adding history.pushState(null,’test’,v.scr); to the var vplay function in 1c-gallery.js, as below. history.pushState, however, isn’t having the back button to work as it should so I’m looking further into that.
v.onclick = () => {
if (!v.classList.contains("full")) {
vplay.toggle(v);
history.pushState(null, 'test', v.src);
}
That
pushState
syntax is wrong, and you need more.1) Add unique ID to every video –
<video id="vidN">
2) Modify JS on click –
2.1) Update URL params
let url = new URL(window.location); url.searchParams.set("id", N);
2.2) Push state
history.pushState({id:N}, "", "gallery.php");
3) On window load and popstate, check for
?id=N
and click the video accordingly –let id = new URL(document.location).searchParams.get("id"); if (id!==null) { document.getElementById("vid"+id).click(); }
https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event
Good luck with your project.
This is a great script, and I managed to get it working, and even sorting with newest uoloads first.
Some of my videos are filmed with the camera in vertical orientation, but since the camera doesn’t know this, they are shown in horizontal orientation.
Is it possible to rotate these videos 90 deg when shown?
I can indetify the vertical videos by som characters in the filenames like this:
if (fnmatch(“*_Vert*”, $file))
but I have not been able to figure out if rotation is possible at all.
You can try to apply CSS rotate on all videos with
_vert
– https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotateBut otherwise, I think the “proper way” is to use a video editor to rotate the videos.
Thank you, that seems to work, but I have of couse some things to change to make it look right. But you pointed me in the right direction 🙂
You are right, that a video editor would be better, but the files are uploaded directly from the camera via FTP.
Hello, many thanks for sharing this excellent code! I have 1 question that I hope you can help with. How can I make it display the file name underneath each video?
Tutorial updated. See above.
Hi, thank you for this. I was hoping it would also grab videos from subfolders. so gallery/1/2/3/4 etc. Can you show me how to do that?
This is what I tried: $dir = __DIR__ . DIRECTORY_SEPARATOR . “{gallery/,gallery/**/,gallery/**/**/,gallery/**/**/**,gallery/**/**/**/**}” . DIRECTORY_SEPARATOR;
GLOB will not read subfolders, the only way is to create a recursive function. See the recursive example here – https://code-boxx.com/list-files-folders-php/
So Simple & Effective. Suits my purpose to a tee.
On thing I tried to change was to make the controls visible by default on the thumb-nails.
Can’t figure how to do that and it’s beyond the scope of my skills.
I see I can right click on them one by one & show controls, then have multiple files playing in thumbnail view. Even better when viewing Sky-Cam time-lapse.
Can you point me in the direction of what I need to change to do the above.
Thanks.
<video controls>
I don’t get it. Could have just done a Google search for “HTML video show controls”, and the answer is right there in 1 minute flat???
The output are not correct if there are spaces in the file names.
Simple fix for the dumb browsers that still don’t do automatic URL encode –
rawurlencode(basename($vid))
.This is so awesome, and exactly what I was looking for in a simple video gallery.
Thanks so much.
However, I do have one question. I am using IFrames on my page, where the php would be within the IFrame.
When doing this, clicking on the video does not expand it to full screen, and I’m wondering what changes to the javascript function would be needed to get it to work in an IFrame?
Javascript
requestFullscreen()
cannot expand beyond the iframe. So 2 possible solutions:1) https://www.tutorialspoint.com/setting-full-screen-iframe-in-javascript
2) Just use a
div
, notiframe
– I don’t see why it is necessary to embed the video gallery in an iframe. That just causes more problems.Thanks. I’ll look at that link.
Also, I should have said html frameset and not iframe. That’s my bad. 🙁
The code I’m using is from 2007 and before the tag, where I have to manually code in every new video clip into the html; which is why I was finally getting around to looking at gallery options such as yours.
The reason for the frames, is that I have a narrow, scrolling menu on the left-hand frame, which when an item was clicked, it would open up an html page in the right-hand frame, which was populated with the video clips corresponding to that menu item. Changing the html pages to your php code works perfectly at auto-generating the videos in the right-hand frame.
I like the idea of having them play full screen, or even just full to the frame size, so I’ll keep looking at ways to get it to work. 🙂
Last piece of advice – Look into modern AJAX and fetch. Frameset is obsolete and not supported in HTML5.
Good luck with your project.
Hey man,
thanks for the awesome script.
Is there any way we can make the thumbnail work on iOS (Safari and Chrome) does not show the preview of the video!
Many Thanks!
Try forcing a preload
<video preload>
, or worst case, manually define a poster<video poster="COVER.JPG">
Great article, very simple and works perfectly.
Quick question, do you have any idea on how I would go about implementing pagination for this example or the image gallery example? I have a lots of files I would like to view using this, but I’ve never used pagination without a DB, or would it be better to just create a DB with the contents of the folder?
I really enjoy your posts, keep up the awesome work my friend!
Thank you!
Well, it’s still possible with some changes.
1) Use PHP
$videos = glob(...)
as usual.2) Use it to generate a JS array instead.
$all = count($videos);
var jsvideos = [<?php foreach($videos as $i=>$v) { echo '"$k"'; if($i<$all) { echo ","; } } >];
3) Create a JS function to generate the gallery instead.
var pagenow = 0; var perpage = 10;
var container = document.getElementById("vid-gallery");
container.innerHTML = "";
for (let i=pagenow*perpage; i<(pagenow+1)*perpage) { let vid = document.createElement("video"); vid.src=jsvideos[i]; container.appendChild(vid); }
4) Lastly, create a dropdown pagination box somewhere that will fire the above function and change the page.
Hi,
This is great post and I was looking for something like this. And further..
How we can set below.
Title for each video
Share option on each video, so users can quickly share on social media
Thanks in advance..
Debeve
That is not “very simple”, but closer to a “full video gallery”. If you need all those features, I will highly suggest doing a search for other available packages… Trying to build all those on top of this one is going to be a pretty crazy process.