JavaScript Photo Editor Project Source Code

How To Create a Photo Editor Application in JavaScript, HTML and CSS

How To Create a Photo Editor Application in JavaScript, HTML and CSS



In this JavaScript Tutorial, we will see how to create a Photo editing application using  JavaScript, HTML5, CSS3.
This photo editor is a single-page application that provides editing features like filters, brightness/contrast adjustment, cropping, and image manipulation.

What We Are Gonna Use In This Project:

- JavaScript Programming Language.
- HTML and CSS.
- Font-awesome.
- Visual Studio Editor.




if you want the source code click on the download button below




Project Source Code:



     - Menu Toggle

Toggles the mobile sidebar menu open/closed and switches between hamburger and X icons.


// Step 4: Set up what happens when someone clicks the hamburger menu button
menuToggle.addEventListener('click', () => {

// Toggle the menu open or closed
toolbar.classList.toggle('open');
overlay.classList.toggle('active');

// When opening the menu, make sure we're scrolled to the top
if (toolbar.classList.contains('open')) {
toolbar.scrollTop = 0;
}

// Change the hamburger icon to an X when open, or back to hamburger when closed
if (toolbar.classList.contains('open')) {
menuToggle.innerHTML = '<i class="fas fa-times"></i>'; // X icon
} else {
menuToggle.innerHTML = '<i class="fas fa-bars"></i>'; // Hamburger icon
}

});




     - Overlay Click
    
Closes the mobile menu when user clicks outside the sidebar area.


// Step 5: Allow users to close the menu by clicking anywhere outside it
overlay.addEventListener('click', () => {
toolbar.classList.remove('open'); // Close the menu
overlay.classList.remove('active'); // Hide the dark overlay
// Change icon back to hamburger
menuToggle.innerHTML = '<i class="fas fa-bars"></i>';
});




     - Mobile Menu Auto-Close.

Automatically closes the mobile menu after tool selection on devices with screen width ≤ 768px.


// Step 6: Function to close the menu on mobile after selecting a tool
function closeMenuIfMobile() {
// Only close the menu if we're on a mobile device (screen width <= 768px)
if (window.innerWidth <= 768) {
toolbar.classList.remove('open');
overlay.classList.remove('active');
menuToggle.innerHTML = '<i class="fas fa-bars"></i>';
}
}












     - Show and Hide Loading Spinner.

Shows a spinning loader icon during image processing operations.
Hides the loading spinner when image processing is complete.


// Step 7: Function to show the loading spinner when processing images
function showLoading() {
loading.style.display = 'block';
}

// Step 8: Function to hide the loading spinner when done processing
function hideLoading() {
loading.style.display = 'none';
}






     - Slider Value Updater.

Updates the percentage display text next to brightness and contrast sliders in real-time.


// Step 9: Function to update the percentage text next to sliders
function updateSliderValue(sliderId, valueId) {
const slider = document.getElementById(sliderId);
const valueSpan = document.getElementById(valueId);
valueSpan.textContent = `${slider.value}%`; // Display current value with % sign
}




     - Filter Application.

Applies all active filters (grayscale, invert, sepia, brightness, contrast) to the canvas image using CSS filter properties.


// Step 10: Apply brightness and contrast filters based on slider positions
function applyFilters() {

if (!img.src) return; // Don't do anything if no image is loaded

// Construct filter string using all active filters
let filterString = '';
if (activeFilters.grayscale > 0) filterString +=
`grayscale(${activeFilters.grayscale}%) `;
if (activeFilters.invert > 0) filterString +=
`invert(${activeFilters.invert}%) `;
if (activeFilters.sepia > 0) filterString +=
`sepia(${activeFilters.sepia}%) `;
filterString += `brightness(${activeFilters.brightness}%)
contrast(${activeFilters.contrast}%)`;

// Apply the combined filters
ctx.filter = filterString;

// Redraw the image with the new filters applied
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

}



     - Resize Canvas.

Dynamically resizes the canvas display to fit within the container while maintaining image aspect ratio.


// Step 11: Make sure the image fits nicely on screen without being too big or small
function resizeCanvasToFitScreen() {

if (!img.src) return; // Don't do anything if no image is loaded

// Find out how much space we have available
const containerWidth = document.getElementById('canvasContainer').clientWidth;
const containerHeight = document.getElementById('canvasContainer').clientHeight;

// Calculate the width-to-height ratio of the image
const aspectRatio = img.width / img.height;

// Start with the original image size
let newWidth = img.width;
let newHeight = img.height;

// If the image is wider than our container
// (with 10% margin), make it smaller
if (newWidth > containerWidth * 0.9) {
newWidth = containerWidth * 0.9;
// Keep the same width-to-height ratio
newHeight = newWidth / aspectRatio;
}

// If the image is taller than our container
// (with 10% margin), make it smaller
if (newHeight > containerHeight * 0.9) {
newHeight = containerHeight * 0.9;
// Keep the same width-to-height ratio
newWidth = newHeight * aspectRatio;
}

// Remember these dimensions for later comparison
currentCanvasWidth = newWidth;
currentCanvasHeight = newHeight;
// Update the display size of the canvas
// (visual only, not the actual pixel dimensions)
canvas.style.width = `${newWidth}px`;
canvas.style.height = `${newHeight}px`;

}





     - Upload Image.

Creates a file input dialog, processes selected image files, and loads them onto the canvas while preserving original data.


// Step 12: Handle the Upload Image button
document.getElementById('uploadBtn').addEventListener('click', () => {

// Create an invisible file input element (the file picker dialog)
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*'; // Allow any image format

// When the user selects a file from their device
input.onchange = (e) => {

const file = e.target.files[0];
// If they canceled without selecting a file, do nothing
if (!file) return;
// This helps us read the selected file
const reader = new FileReader();
showLoading(); // Show the spinner while we load the image

// When the file finishes loading into memory
reader.onload = function(event) {

// Set our image source to the loaded file data
img.src = event.target.result;

// Once the image is fully processed and ready to use
img.onload = function() {

// Set the canvas size to match the actual image dimensions (not display size)
canvas.width = img.width;
canvas.height = img.height;

// Draw the image onto the canvas
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

// Save a backup of the original image for the reset button
originalImage = ctx.getImageData(0, 0, canvas.width, canvas.height);

// Reset sliders to default position (100%)
brightnessSlider.value = 100;
contrastSlider.value = 100;

// Update slider value displays
updateSliderValue('brightnessSlider', 'brightnessValue');
updateSliderValue('contrastSlider', 'contrastValue');

// Adjust the display size to fit nicely on screen
resizeCanvasToFitScreen();

hideLoading(); // Hide the spinner now that we're done
closeMenuIfMobile(); // Close the menu if on mobile

}

};

// Start reading the selected image file
reader.readAsDataURL(file);

};

// Open the file selection dialog
input.click();

});





     - Grayscale Toggle.

Toggles black and white filter on/off for the current image.


// Step 13: Apply black & white filter when grayscale button is clicked
document.getElementById('grayscaleBtn').addEventListener('click', () => {
if (!img.src) return; // Don't do anything if no image is loaded

// Toggle grayscale filter
activeFilters.grayscale = activeFilters.grayscale === 100 ? 0 : 100;
applyFilters();
closeMenuIfMobile();
});





     - Color Inversion Toggle.

Toggles color inversion filter (negative effect) on/off for the current image.


// Step 14: Invert all colors in the image (like a negative)
document.getElementById('invertBtn').addEventListener('click', () => {
if (!img.src) return; // Don't do anything if no image is loaded
// Toggle invert filter
activeFilters.invert = activeFilters.invert === 100 ? 0 : 100;
applyFilters();
closeMenuIfMobile();
});





     - Sepia Tone Toggle

Toggles vintage sepia tone filter on/off for the current image.


// Step 15: Apply sepia filter
document.getElementById('sepiaBtn').addEventListener('click', () => {
if (!img.src) return; // Don't do anything if no image is loaded
// Toggle sepia filter
activeFilters.sepia = activeFilters.sepia === 100 ? 0 : 100;
applyFilters();
closeMenuIfMobile();
});








     - Brightness Control

Adjusts image brightness in real-time as user moves the brightness slider (0-200%).


// Step 16: Set up the brightness slider
const brightnessSlider = document.getElementById('brightnessSlider');
brightnessSlider.addEventListener('input', () => {
// Update the percentage display
updateSliderValue('brightnessSlider', 'brightnessValue');
activeFilters.brightness = brightnessSlider.value;
applyFilters();

});






     - Brightness Mobile

Closes mobile menu automatically after brightness adjustment is complete.


// Step 17: Close the menu after adjusting brightness on mobile devices
brightnessSlider.addEventListener('change', () => {
if (window.innerWidth <= 768) {
// Wait a short moment before closing (feels more natural)
setTimeout(closeMenuIfMobile, 300);
}
});





     - Contrast Control

Adjusts image contrast in real-time as user moves the contrast slider (0-200%).


// Step 18: Set up the contrast slider
const contrastSlider = document.getElementById('contrastSlider');
contrastSlider.addEventListener('input', () => {
// Update the percentage display
updateSliderValue('contrastSlider', 'contrastValue');
activeFilters.contrast = contrastSlider.value;
applyFilters();

});



     - Contrast Mobile

Closes mobile menu automatically after contrast adjustment is complete.


// Step 19: Close the menu after adjusting contrast on mobile devices
contrastSlider.addEventListener('change', () => {
if (window.innerWidth <= 768) {
// Wait a short moment before closing (feels more natural)
setTimeout(closeMenuIfMobile, 300);
}
});






     - Crop Mode Activator

Enables crop selection mode and activates the Apply Crop button.


// Step 20: Enter crop mode when crop button is clicked
document.getElementById('cropBtn').addEventListener('click', () => {

if (!img.src) return; // Don't do anything if no image is loaded

cropActive = true; // Turn on crop mode
// Make the Apply Crop button clickable
document.getElementById('applyCropBtn').classList.remove('disabled');
closeMenuIfMobile();

});





     - Coordinate Converter

Converts screen mouse/touch coordinates to actual canvas image coordinates for accurate crop selection.


// Step 21: Function to convert screen coordinates to actual image coordinates
function getScaledCoordinates(e) {
// Get the canvas position and size on screen
const rect = canvas.getBoundingClientRect();

// Calculate the scaling ratio between screen size and actual image size
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;

// Convert the screen coordinates to actual image coordinates
return {
x: (e.clientX - rect.left) * scaleX,
y: (e.clientY - rect.top) * scaleY
};
}





     - Crop Selection Start

Initiates crop rectangle selection when user clicks/touches the canvas in crop mode.


// Step 22: Handle start of crop selection (mouse down or touch start)
canvas.addEventListener('mousedown', handlePointerStart);
canvas.addEventListener('touchstart', (e) => {

if (!img.src) return; // Don't do anything if no image is loaded

e.preventDefault(); // Prevent default touch behavior
const touch = e.touches[0]; // Get the first touch point

// Pass the touch coordinates to our handler function
handlePointerStart({
clientX: touch.clientX,
clientY: touch.clientY
});

});

function handlePointerStart(e) {
if (!cropActive || !img.src) return; // Only proceed if in crop mode with an image

isDragging = true; // Start dragging mode

// Save the starting point for our crop rectangle
const coords = getScaledCoordinates(e);
cropRect.startX = coords.x;
cropRect.startY = coords.y;
}






     - Crop Selection Update

Updates crop rectangle size and position as user drags mouse/finger across the canvas.


// Step 23: Update crop selection while dragging
canvas.addEventListener('mousemove', handlePointerMove);
canvas.addEventListener('touchmove', (e) => {

if (!cropActive || !isDragging || !img.src) return;

e.preventDefault(); // Prevent default touch behavior like scrolling
const touch = e.touches[0]; // Get the first touch point

// Pass the touch coordinates to our handler function
handlePointerMove({
clientX: touch.clientX,
clientY: touch.clientY
});

});

function handlePointerMove(e) {
if (!isDragging || !cropActive || !img.src) return;

// Get current pointer position
const coords = getScaledCoordinates(e);

// Calculate the width and height of our crop selection
cropRect.width = coords.x - cropRect.startX;
cropRect.height = coords.y - cropRect.startY;

// Redraw the image and draw our crop selection rectangle on top
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
ctx.drawImage(img, 0, 0, canvas.width, canvas.height); // Redraw the image
ctx.strokeStyle = '#03dac6'; // Teal color for the crop rectangle
ctx.lineWidth = 2; // 2 pixels thick line
            // Draw the rectangle
ctx.strokeRect(cropRect.startX, cropRect.startY, cropRect.width, cropRect.height);
}





     - Crop Selection End

Finalizes crop rectangle selection when user releases mouse button or lifts finger.


// Step 24: End crop selection when pointer is released or leaves canvas
canvas.addEventListener('mouseup', handlePointerEnd);
canvas.addEventListener('touchend', handlePointerEnd);
canvas.addEventListener('mouseleave', handlePointerEnd);
canvas.addEventListener('touchcancel', handlePointerEnd);

function handlePointerEnd() {
if (cropActive) isDragging = false; // Stop dragging mode
}




     - Crop Processor

Extracts selected crop area, resizes canvas, and replaces current image with cropped version.


// Step 25: Process the crop operation when Apply Crop button is clicked
document.getElementById('applyCropBtn').addEventListener('click', () => {

if (!img.src) return; // Don't do anything if no image is loaded

// Only proceed if in crop mode and we have a valid selection area
if (cropActive && cropRect.width && cropRect.height) {
// Handle negative width/height if user dragged in reverse direction
const positiveWidth = Math.abs(cropRect.width);
const positiveHeight = Math.abs(cropRect.height);

// Find the correct starting position (top-left corner of selection)
const startX = cropRect.width > 0 ? cropRect.startX :
cropRect.startX + cropRect.width;
const startY = cropRect.height > 0 ? cropRect.startY :
cropRect.startY + cropRect.height;

// Extract just the selected portion of the image
const croppedImage = ctx.getImageData(startX, startY,
positiveWidth, positiveHeight);

// Resize the canvas to match the cropped size
canvas.width = positiveWidth;
canvas.height = positiveHeight;

// Place the cropped portion on the canvas
ctx.putImageData(croppedImage, 0, 0);

// Create a temporary canvas to help create a new image
const tempCanvas = document.createElement('canvas');
tempCanvas.width = positiveWidth;
tempCanvas.height = positiveHeight;
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
tempCtx.putImageData(croppedImage, 0, 0);

// Create a new image from the cropped canvas
const newImg = new Image();
newImg.onload = function() {

img = newImg; // Update our main image to be the cropped version

// Exit crop mode
cropActive = false;
document.getElementById('applyCropBtn').classList.add('disabled');

// Resize to fit screen
resizeCanvasToFitScreen();
closeMenuIfMobile();

};

newImg.src = tempCanvas.toDataURL(); // Convert canvas to image data

}

});




     - Image Reset

Restores the original uploaded image and resets all filters and adjustments to default values.


// Step 26: Reset to original image when Reset button is clicked
document.getElementById('resetBtn').addEventListener('click', () => {
// Don't do anything if no image or backup
if (!img.src || !originalImage) return;
// Reset all filter values
activeFilters = {
grayscale: 0,
invert: 0,
sepia: 0,
brightness: 100,
contrast: 100
};
// Reset UI sliders
brightnessSlider.value = 100;
contrastSlider.value = 100;
// Update slider displays
updateSliderValue('brightnessSlider', 'brightnessValue');
updateSliderValue('contrastSlider', 'contrastValue');
// Restore canvas dimensions and original image
canvas.width = originalImage.width;
canvas.height = originalImage.height;
ctx.putImageData(originalImage, 0, 0);
// Reset filter
ctx.filter = 'none';
// Create a new image from the reset canvas
const newImg = new Image();
newImg.onload = function() {
img = newImg;
resizeCanvasToFitScreen();
};
newImg.src = canvas.toDataURL();
closeMenuIfMobile();

});



     - Image Saver

Downloads the current edited image as a PNG file to user's device.


// Step 27: Save the edited image when Save button is clicked
document.getElementById('saveBtn').addEventListener('click', () => {

if (!img.src) return; // Don't do anything if no image is loaded

// Create a download link
const link = document.createElement('a');
link.href = canvas.toDataURL('image/png'); // Convert canvas to PNG format
link.download = 'edited-image.png'; // Default filename for download
link.click(); // Simulate a click to trigger the download

closeMenuIfMobile();

});





Periodic Table In Java Swing

How to Create a Periodic Table in Java Netbeans

How to Create a Periodic Table in Java Netbeans



In this Java Tutorial we will see How To Create a Periodic Table that displays all 118 chemical elements with hover tooltips and gradient backgrounds In Java Using Netbeans.

What We Are Gonna Use In This Project:

- Java Programming Language.
- NetBeans Editor.





Project Source Code:



/**
 *
 * @author 1BestCsharp
 */
public class PeriodicTable extends JFrame {

        // A map to store all chemical elements with their symbols as keys
        private final Map<String, Element> elements = new HashMap<>();
        // Custom tooltip to display element information
        private CustomTooltip tooltip;
    
        public PeriodicTable(){
            
                // Set basic JFrame properties
                setTitle("Periodic Table");
                setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                setLayout(new BorderLayout());
                
                // Load all element data into the elements map
                initializeElements();

                // Create a tooltip that will appear when hovering over elements
                tooltip = new CustomTooltip();
                tooltip.setVisible(false);
                
                  // Create the main panel with a gradient background
                 JPanel mainPanel = new JPanel() {
                             
                         /**
                        * Override the paintComponent method to create a custom gradient background
                        */
                       @Override
                       protected void paintComponent(Graphics g) {
                           
                                   super.paintComponent(g);
                                // Cast to Graphics2D to access advanced drawing features
                                Graphics2D g2d = (Graphics2D) g;
                                // Enable anti-aliasing for smoother edges
                                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                                int w = getWidth();
                                int h = getHeight();
                                // Create dark blue to darker blue gradient
                                GradientPaint gp = new GradientPaint(0, 0, new Color(30, 33, 39), w, h, new Color(15, 15, 18));
                                g2d.setPaint(gp);
                                g2d.fillRect(0, 0, w, h);
                       
                       }
                 };
                 
                  // Set up a grid layout for the periodic table (10 rows, 18 columns)
                mainPanel.setLayout(new GridLayout(10, 18, 5, 5));
                mainPanel.setBackground(Color.WHITE);
                mainPanel.setOpaque(true);
                
                // Create and place element buttons in the correct positions in the grid
                for (int i = 0; i < 10; i++) {
                    
                      for (int j = 0; j < 18; j++) {
                          // Get the element symbol at this position (if any)
                         String elementSymbol = getElementSymbol(i, j);
                         if (elementSymbol != null) {
                                // Create a button for this element
                                JButton button = createElementButton(elementSymbol);
                                mainPanel.add(button);
                          }  else {
                                // Add an empty panel for positions with no element
                                JPanel emptyPanel = new JPanel();
                                emptyPanel.setOpaque(false);
                                mainPanel.add(emptyPanel);
                            }
                      }
                    
                }
                
                
                 // Add the main panel to the center of the JFrame
                add(mainPanel, BorderLayout.CENTER);
                
                 // Set the window size and center it on the screen
                setSize(1600, 1000);
                setLocationRelativeTo(null);

        }
        
        
        /**
        * Creates a custom button for a chemical element
        * 
        * @param symbol The chemical symbol of the element (e.g., "H" for Hydrogen)
        * @return A JButton customized for the element
        */
         private JButton createElementButton(String symbol) {
         
                  // Create a custom JButton with overridden paintComponent method
                 JButton button = new JButton() {
                 
                        /**
                        * Custom rendering for element buttons with rounded corners and gradient
                        */
                       @Override
                       protected void paintComponent(Graphics g) {
                                    Graphics2D g2d = (Graphics2D) g;
                                    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                            RenderingHints.VALUE_ANTIALIAS_ON);
                                    
                                   // Get the element data
                                    Element element = elements.get(symbol);
                                    // Get appropriate color based on element category
                                    Color bgColor = getCategoryColor(element.category); 
                                    
                                   // Create gradient background with a rounded rectangle shape
                                    GradientPaint gradient = new GradientPaint(
                                        0, 0, 
                                        bgColor.darker(), 
                                        getWidth(), 
                                        getHeight(), 
                                       bgColor.brighter()
                                    );
                                    g2d.setPaint(gradient);
                                    g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 16, 16);
                                    
                                    // Add shadow effect to give depth
                                    g2d.setColor(new Color(0, 0, 0, 50)); // Semi-transparent black
                                    g2d.fillRoundRect(6, 6, getWidth() - 12, getHeight() - 12, 12, 12);
                                    
                                   // Draw the atomic number in the top-left corner
                                    g2d.setColor(Color.WHITE);
                                    g2d.setFont(new Font("Segoe UI", Font.PLAIN, 12));
                                    g2d.drawString(String.valueOf(element.atomicNumber), 12, 20);
                                    
                                    // Draw the element symbol in the center
                                    g2d.setFont(new Font("Segoe UI", Font.BOLD, 20));
                                    FontMetrics fm = g2d.getFontMetrics();
                                    int symbolWidth = fm.stringWidth(symbol);
                                    int symbolX = (getWidth() - symbolWidth) / 2;
                                    g2d.drawString(symbol, symbolX, getHeight() / 2 + 7);

                       }
                     
                 };
                 
                  // Set button properties
                 button.setPreferredSize(new Dimension(100, 100));
                 button.setBorderPainted(false);
                 button.setFocusPainted(false);
                 button.setContentAreaFilled(false);
                 
                 // Add mouse listeners 
                 button.addMouseListener(new MouseAdapter() {
                 
                            /**
                            * Show tooltip with element details when mouse hovers over the button
                            */
                           @Override
                           public void mouseEntered(MouseEvent e) {
                               Element element = elements.get(symbol);
                               showTooltip(element, e.getLocationOnScreen());
                               button.setCursor(new Cursor(Cursor.HAND_CURSOR));
                           }
                           
                           /**
                            * Hide tooltip when mouse leaves the button
                            */
                           @Override
                           public void mouseExited(MouseEvent e) {
                               tooltip.setVisible(false);
                           }
                     
                 });
                 
                 
                 return button;
             
         }
        
        
     /**
     * Displays a tooltip with detailed information about an element
     * 
     * @param element The Element object containing the data to display
     * @param location The screen location where the mouse entered the button
     */
      private void showTooltip(Element element, Point location) {
      
             // Update tooltip content with element information
             tooltip.updateContent(element);
          
            // Get the current size of the tooltip
            Dimension tooltipSize = tooltip.getSize();
            
            // Calculate position for the tooltip (offset from mouse pointer)
            int x = location.x + 15;
            int y = location.y - tooltipSize.height - 15;
            
           // Get screen dimensions to ensure tooltip stays on screen
           int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
           
            // Adjust y-coordinate if tooltip would go off the top of the screen
            if (y < 0) {
                y = location.y + 15;
            }
          
            // Adjust x-coordinate if tooltip would go off the right side of the screen
            if (x > 0) {
                if (x + tooltipSize.width > screenWidth + (tooltipSize.width / 4)) {
                    x = screenWidth - tooltipSize.width + (tooltipSize.width / 2);
                } else { 
                    x = location.x + 15; 
                } 
            }
            
            // Set the tooltip's location and fade it in with animation
            tooltip.setLocation(x, y);
            tooltip.fadeIn(x, y);
            
      }
         
         
         
         
      /**
     * Custom tooltip window class with fancy styling and animation effects
     */
      private static class CustomTooltip extends JWindow {
          
                  // UI components for the tooltip
                private final JPanel contentPane;
                private final JLabel titleLabel;
                private final JLabel detailsLabel;
                private final JLabel descriptionLabel;
        
                // Animation properties
                private javax.swing.Timer fadeTimer;
                private float opacity = 0.0f;
                private static final int ANIMATION_DURATION = 200; // milliseconds
                private static final int ANIMATION_STEPS = 20;
                
                /**
                * Constructor - Creates a custom tooltip with styling
                */
                 public CustomTooltip() {
                           // Make the window background transparent
                           setBackground(new Color(0, 0, 0, 0));
                           
                          // Create custom panel with painting overridden for effects
                          contentPane = new JPanel() {
                              
                                /**
                                * Custom painting for the tooltip
                                */
                               @Override
                               protected void paintComponent(Graphics g) {
                                   
                                   Graphics2D g2d = (Graphics2D) g;
                                    // Enable high quality rendering settings
                                    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                                            RenderingHints.VALUE_ANTIALIAS_ON);
                                    g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, 
                                            RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
                                    g2d.setRenderingHint(RenderingHints.KEY_RENDERING, 
                                            RenderingHints.VALUE_RENDER_QUALITY);

                                   // Create main background shape with rounded corners
                                    int width = getWidth();
                                    int height = getHeight();
                                    RoundRectangle2D.Float background = new RoundRectangle2D.Float(
                                            0, 0, 
                                            width - 1, 
                                            height - 1, 
                                            16, 
                                            16
                                    );
                                    
                                    // Create drop shadow effect with fading based on animation
                                    g2d.setComposite(AlphaComposite.getInstance(
                                            AlphaComposite.SRC_OVER, opacity * 0.4f));
                                    g2d.setColor(Color.BLACK);
                                    g2d.fill(new RoundRectangle2D.Float(4, 4, width - 1, height - 1, 16, 16));

                                    // Create gradient background with animated opacity
                                    g2d.setComposite(AlphaComposite.getInstance(
                                            AlphaComposite.SRC_OVER, opacity));
                                    
                                    Paint gradientPaint = new LinearGradientPaint(
                                        0, 0,
                                        0, height,
                                        new float[]{0f, 1f}, // Gradient stops
                                        new Color[]{
                                            new Color(40, 44, 52), // Top color
                                            new Color(30, 33, 40)  // Bottom color
                                        }
                                    );
                                    g2d.setPaint(gradientPaint);
                                    g2d.fill(background);
                                    
                                    // Add subtle inner glow effect
                                    g2d.setComposite(AlphaComposite.getInstance(
                                            AlphaComposite.SRC_OVER, opacity * 0.1f));
                                    g2d.setColor(Color.WHITE);
                                    g2d.setStroke(new BasicStroke(15f));
                                    g2d.draw(new RoundRectangle2D.Float(2, 2, width - 5, height - 5, 15, 15));

                                    // Add border with semi-transparent white
                                    g2d.setComposite(AlphaComposite.getInstance(
                                            AlphaComposite.SRC_OVER, opacity * 0.5f));
                                    g2d.setColor(new Color(140, 140, 140));
                                    g2d.setStroke(new BasicStroke(2f));
                                    g2d.draw(background);
                    
                                   // Reset composite to fully opaque for content
                                    g2d.setComposite(AlphaComposite.getInstance(
                                            AlphaComposite.SRC_OVER, 1f));

                               }
                              
                          };
                          
                            // Set up the content panel with vertical layout
                            contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
                            contentPane.setOpaque(false);
                            contentPane.setBorder(BorderFactory.createEmptyBorder(16, 20, 16, 20));
                            
                            // Create title label with bold styling
                            titleLabel = new JLabel();
                            titleLabel.setForeground(new Color(225, 225, 225, 1));
                            titleLabel.setFont(new Font("Segoe UI", Font.BOLD, 20));
                            titleLabel.setAlignmentX(Component.LEFT_ALIGNMENT);

                            // Create details label for atomic properties
                            detailsLabel = new JLabel();
                            detailsLabel.setForeground(new Color(200, 200, 200));
                            detailsLabel.setFont(new Font("Segoe UI", Font.PLAIN, 14));
                            detailsLabel.setAlignmentX(Component.LEFT_ALIGNMENT);

                            // Create description label for element info
                            descriptionLabel = new JLabel();
                            descriptionLabel.setForeground(new Color(160, 160, 160));
                            descriptionLabel.setFont(new Font("Segoe UI", Font.ITALIC, 14));
                            descriptionLabel.setAlignmentX(Component.LEFT_ALIGNMENT);

                            // Add components to the panel with spacing
                            contentPane.add(titleLabel);
                            contentPane.add(Box.createVerticalStrut(12)); // Add spacing
                            contentPane.add(detailsLabel);
                            contentPane.add(Box.createVerticalStrut(8));  // Add spacing
                            contentPane.add(descriptionLabel);
                            
                           // Set the content pane for this window
                           setContentPane(contentPane);

                            // Initialize animation timer for fade-in effect
                            fadeTimer = new javax.swing.Timer(
                                    ANIMATION_DURATION / ANIMATION_STEPS, (ActionEvent e) -> {
                                        
                                    opacity = Math.min(1.0f, opacity + (1.0f / ANIMATION_STEPS));
                                    repaint();
                                    if (opacity >= 1.0f) {
                                        fadeTimer.stop();
                                    }
                            });

                 }
                 
                /**
                * Animates the tooltip fading in at the specified location
                * 
                * @param x X-coordinate for tooltip position
                * @param y Y-coordinate for tooltip position
                */
               public void fadeIn(int x, int y) {
                   setLocation(x, y);
                   opacity = 0.0f; // Start fully transparent
                   setVisible(true);
                   fadeTimer.restart(); // Begin fade-in animation
               }
               
               
            /**
            * Updates the tooltip content with element information
            * 
            * @param element The Element object containing data to display
            */
            public void updateContent(Element element) {
                    // Add an icon and set the element name and symbol as the title
                    String iconText = "⚛ ";  // Unicode atom symbol
                    titleLabel.setText(String.format(
                            "<html>%s%s <span style='color: #88B8FF'>(%s)</span></html>",
                        iconText, element.name, element.symbol));
                    
                    // Format detailed information with HTML formatting
                    detailsLabel.setText(String.format("<html>" +
                        "<span style='color: #88B8FF'>Atomic Number:</span> %d<br>" +
                        "<span style='color: #88B8FF'>Atomic Mass:</span> %.3f<br>" +
                        "<span style='color: #88B8FF'>Category:</span> %s" +
                        "</html>",
                        element.atomicNumber, element.atomicMass, element.category));
                    
                     // Wrap description text with HTML for proper text wrapping
                    descriptionLabel.setText("<html><div style='width: 300px'>" + element.description + "</div></html>");
                    
                    // Resize the tooltip to fit the content
                    pack();
                    
                    // Ensure minimum width for consistent appearance
                    setSize(Math.max(getWidth(), 400), getHeight());

            }

      }
         
         
         
         
         
         
     
     /**
     * Returns an appropriate color for each element category
     * 
     * @param category The element category (e.g., "Noble Gas", "Alkali Metal")
     * @return Color object representing the category
     */
    private Color getCategoryColor(String category) {
        // Use switch expression to map categories to colors
        return switch (category) {
            case "Alkali Metal" -> new Color(255, 105, 97);          // Reddish
            case "Alkaline Earth Metal" -> new Color(255, 178, 102); // Orange
            case "Transition Metal" -> new Color(255, 228, 105);     // Yellow
            case "Post-Transition Metal" -> new Color(158, 216, 105); // Light green
            case "Metalloid" -> new Color(105, 255, 178);            // Turquoise
            case "Nonmetal" -> new Color(105, 228, 255);            // Light blue
            case "Noble Gas" -> new Color(105, 153, 255);           // Blue
            case "Lanthanide" -> new Color(178, 105, 255);          // Purple
            case "Actinide" -> new Color(255, 105, 255);            // Pink
            default -> new Color(200, 200, 200);                    // Grey (default)
        };
    }
         
         
        
        
        
     /**
     * Initializes the elements HashMap with all periodic table elements
     * This method creates Element objects for all chemical elements
     */
      private void initializeElements() {
      
                  // Row 1
                addElement(1, "H", "Hydrogen", 1.008, "Nonmetal", "Lightest element");
                addElement(2, "He", "Helium", 4.003, "Noble Gas", "Inert gas");
                
                // Row 2
                addElement(3, "Li", "Lithium", 6.941, "Alkali Metal", "Highly reactive metal");
                addElement(4, "Be", "Beryllium", 9.012, "Alkaline Earth Metal", "Toxic metal");
                addElement(5, "B", "Boron", 10.811, "Metalloid", "Semiconductor");
                addElement(6, "C", "Carbon", 12.011, "Nonmetal", "Basis for organic chemistry");
                addElement(7, "N", "Nitrogen", 14.007, "Nonmetal", "Essential for life");
                addElement(8, "O", "Oxygen", 15.999, "Nonmetal", "Essential for respiration");
                addElement(9, "F", "Fluorine", 18.998, "Nonmetal", "Most reactive nonmetal");
                addElement(10, "Ne", "Neon", 20.180, "Noble Gas", "Used in signs");
                
                 // Row 3
                addElement(11, "Na", "Sodium", 22.990, "Alkali Metal", "Highly reactive metal");
                addElement(12, "Mg", "Magnesium", 24.305, "Alkaline Earth Metal", "Light structural metal");
                addElement(13, "Al", "Aluminum", 26.982, "Post-Transition Metal", "Common structural metal");
                addElement(14, "Si", "Silicon", 28.086, "Metalloid", "Semiconductor");
                addElement(15, "P", "Phosphorus", 30.974, "Nonmetal", "Essential for life");
                addElement(16, "S", "Sulfur", 32.065, "Nonmetal", "Yellow nonmetal");
                addElement(17, "Cl", "Chlorine", 35.453, "Nonmetal", "Toxic green gas");
                addElement(18, "Ar", "Argon", 39.948, "Noble Gas", "Inert gas");
                
                // Row 4
                addElement(19, "K", "Potassium", 39.098, "Alkali Metal", "Essential for life");
                addElement(20, "Ca", "Calcium", 40.078, "Alkaline Earth Metal", "Essential for bones");
                addElement(21, "Sc", "Scandium", 44.956, "Transition Metal", "Rare earth metal");
                addElement(22, "Ti", "Titanium", 47.867, "Transition Metal", "Strong light metal");
                addElement(23, "V", "Vanadium", 50.942, "Transition Metal", "Hard metal");
                addElement(24, "Cr", "Chromium", 51.996, "Transition Metal", "Shiny hard metal");
                addElement(25, "Mn", "Manganese", 54.938, "Transition Metal", "Hard brittle metal");
                addElement(26, "Fe", "Iron", 55.845, "Transition Metal", "Magnetic metal");
                addElement(27, "Co", "Cobalt", 58.933, "Transition Metal", "Magnetic metal");
                addElement(28, "Ni", "Nickel", 58.693, "Transition Metal", "Hard metal");
                addElement(29, "Cu", "Copper", 63.546, "Transition Metal", "Conductive metal");
                addElement(30, "Zn", "Zinc", 65.380, "Transition Metal", "Blue-white metal");
                addElement(31, "Ga", "Gallium", 69.723, "Post-Transition Metal", "Low melting point");
                addElement(32, "Ge", "Germanium", 72.640, "Metalloid", "Semiconductor");
                addElement(33, "As", "Arsenic", 74.922, "Metalloid", "Toxic metalloid");
                addElement(34, "Se", "Selenium", 78.960, "Nonmetal", "Photovoltaic");
                addElement(35, "Br", "Bromine", 79.904, "Nonmetal", "Liquid nonmetal");
                addElement(36, "Kr", "Krypton", 83.798, "Noble Gas", "Inert gas");
                
                // Row 5
                addElement(37, "Rb", "Rubidium", 85.468, "Alkali Metal", "Soft silver metal");
                addElement(38, "Sr", "Strontium", 87.620, "Alkaline Earth Metal", "Soft metal");
                addElement(39, "Y", "Yttrium", 88.906, "Transition Metal", "Rare earth metal");
                addElement(40, "Zr", "Zirconium", 91.224, "Transition Metal", "Hard metal");
                addElement(41, "Nb", "Niobium", 92.906, "Transition Metal", "Superconductor");
                addElement(42, "Mo", "Molybdenum", 95.960, "Transition Metal", "High melting point");
                addElement(43, "Tc", "Technetium", 98.000, "Transition Metal", "Radioactive metal");
                addElement(44, "Ru", "Ruthenium", 101.070, "Transition Metal", "Hard white metal");
                addElement(45, "Rh", "Rhodium", 102.906, "Transition Metal", "Noble metal");
                addElement(46, "Pd", "Palladium", 106.420, "Transition Metal", "Noble metal");
                addElement(47, "Ag", "Silver", 107.868, "Transition Metal", "Precious metal");
                addElement(48, "Cd", "Cadmium", 112.411, "Transition Metal", "Toxic metal");
                addElement(49, "In", "Indium", 114.818, "Post-Transition Metal", "Soft metal");
                addElement(50, "Sn", "Tin", 118.710, "Post-Transition Metal", "Low melting point");
                addElement(51, "Sb", "Antimony", 121.760, "Metalloid", "Semiconductor");
                addElement(52, "Te", "Tellurium", 127.600, "Metalloid", "Semiconductor");
                addElement(53, "I", "Iodine", 126.904, "Nonmetal", "Essential nutrient");
                addElement(54, "Xe", "Xenon", 131.293, "Noble Gas", "Inert gas");
                
                // Row 6
                addElement(55, "Cs", "Cesium", 132.905, "Alkali Metal", "Most reactive metal");
                addElement(56, "Ba", "Barium", 137.327, "Alkaline Earth Metal", "Reactive metal");
                addElement(57, "La", "Lanthanum", 138.905, "Lanthanide", "Rare earth metal");
                addElement(58, "Ce", "Cerium", 140.116, "Lanthanide", "Rare earth metal");
                addElement(59, "Pr", "Praseodymium", 140.908, "Lanthanide", "Rare earth metal");
                addElement(60, "Nd", "Neodymium", 144.242, "Lanthanide", "Magnetic rare earth");
                addElement(61, "Pm", "Promethium", 145.000, "Lanthanide", "Radioactive metal");
                addElement(62, "Sm", "Samarium", 150.360, "Lanthanide", "Rare earth metal");
                addElement(63, "Eu", "Europium", 151.964, "Lanthanide", "Rare earth metal");
                addElement(64, "Gd", "Gadolinium", 157.250, "Lanthanide", "Rare earth metal");
                addElement(65, "Tb", "Terbium", 158.925, "Lanthanide", "Rare earth metal");
                addElement(66, "Dy", "Dysprosium", 162.500, "Lanthanide", "Rare earth metal");
                addElement(67, "Ho", "Holmium", 164.930, "Lanthanide", "Rare earth metal");
                addElement(68, "Er", "Erbium", 167.259, "Lanthanide", "Rare earth metal");
                addElement(69, "Tm", "Thulium", 168.934, "Lanthanide", "Rare earth metal");
                addElement(70, "Yb", "Ytterbium", 173.054, "Lanthanide", "Rare earth metal");
                addElement(71, "Lu", "Lutetium", 174.967, "Lanthanide", "Rare earth metal");
                addElement(72, "Hf", "Hafnium", 178.490, "Transition Metal", "High melting point");
                addElement(73, "Ta", "Tantalum", 180.948, "Transition Metal", "Hard metal");
                addElement(74, "W", "Tungsten", 183.840, "Transition Metal", "Highest melting point");
                addElement(75, "Re", "Rhenium", 186.207, "Transition Metal", "Heavy metal");
                addElement(76, "Os", "Osmium", 190.230, "Transition Metal", "Densest natural element");
                addElement(77, "Ir", "Iridium", 192.217, "Transition Metal", "Hard dense metal");
                addElement(78, "Pt", "Platinum", 195.084, "Transition Metal", "Noble metal");
                addElement(79, "Au", "Gold", 196.967, "Transition Metal", "Precious metal");
                addElement(80, "Hg", "Mercury", 200.590, "Transition Metal", "Liquid metal");
                addElement(81, "Tl", "Thallium", 204.383, "Post-Transition Metal", "Toxic metal");
                addElement(82, "Pb", "Lead", 207.200, "Post-Transition Metal", "Heavy metal");
                addElement(83, "Bi", "Bismuth", 208.980, "Post-Transition Metal", "Slightly radioactive");
                addElement(84, "Po", "Polonium", 209.000, "Post-Transition Metal", "Radioactive metal");
                addElement(85, "At", "Astatine", 210.000, "Metalloid", "Radioactive halogen");
                addElement(86, "Rn", "Radon", 222.000, "Noble Gas", "Radioactive gas");
                
                // Row 7
                addElement(87, "Fr", "Francium", 223.000, "Alkali Metal", "Highly radioactive");
                addElement(88, "Ra", "Radium", 226.000, "Alkaline Earth Metal", "Radioactive metal");
                addElement(89, "Ac", "Actinium", 227.000, "Actinide", "Radioactive metal");
                addElement(90, "Th", "Thorium", 232.038, "Actinide", "Radioactive metal");
                addElement(91, "Pa", "Protactinium", 231.036, "Actinide", "Radioactive metal");
                addElement(92, "U", "Uranium", 238.029, "Actinide", "Nuclear fuel");
                addElement(93, "Np", "Neptunium", 237.000, "Actinide", "Radioactive metal");
                addElement(94, "Pu", "Plutonium", 244.000, "Actinide", "Nuclear fuel");
                addElement(95, "Am", "Americium", 243.000, "Actinide", "Radioactive metal");
                addElement(96, "Cm", "Curium", 247.000, "Actinide", "Radioactive metal");
                addElement(97, "Bk", "Berkelium", 247.000, "Actinide", "Radioactive metal");
                addElement(98, "Cf", "Californium", 251.000, "Actinide", "Radioactive metal");
                addElement(99, "Es", "Einsteinium", 252.000, "Actinide", "Radioactive metal");
                addElement(100, "Fm", "Fermium", 257.000, "Actinide", "Radioactive metal");
                addElement(101, "Md", "Mendelevium", 258.000, "Actinide", "Radioactive metal");
                addElement(102, "No", "Nobelium", 259.000, "Actinide", "Radioactive metal");
                addElement(103, "Lr", "Lawrencium", 262.000, "Actinide", "Radioactive metal");
                addElement(104, "Rf", "Rutherfordium", 267.000, "Transition Metal", "Synthetic element");
                addElement(105, "Db", "Dubnium", 268.000, "Transition Metal", "Synthetic element");
                addElement(106, "Sg", "Seaborgium", 269.000, "Transition Metal", "Synthetic element");
                addElement(107, "Bh", "Bohrium", 270.000, "Transition Metal", "Synthetic element");
                addElement(108, "Hs", "Hassium", 269.000, "Transition Metal", "Synthetic element");
                addElement(109, "Mt", "Meitnerium", 278.000, "Transition Metal", "Synthetic element");
                addElement(110, "Ds", "Darmstadtium", 281.000, "Transition Metal", "Synthetic element");
                addElement(111, "Rg", "Roentgenium", 282.000, "Transition Metal", "Synthetic element");
                addElement(112, "Cn", "Copernicium", 285.000, "Transition Metal", "Synthetic element");
                addElement(113, "Nh", "Nihonium", 286.000, "Post-Transition Metal", "Synthetic element");
                addElement(114, "Fl", "Flerovium", 289.000, "Post-Transition Metal", "Synthetic element");
                addElement(115, "Mc", "Moscovium", 290.000, "Post-Transition Metal", "Synthetic element");
                addElement(116, "Lv", "Livermorium", 293.000, "Post-Transition Metal", "Synthetic element");
                addElement(117, "Ts", "Tennessine", 294.000, "Metalloid", "Synthetic element");
                addElement(118, "Og", "Oganesson", 294.000, "Noble Gas", "Synthetic element");
      
      }
        
        
      
         /**
        * Returns the chemical symbol of the element at the specified position in the periodic table.
        * This method uses a 2D array representation of the periodic table to determine
        * which element (if any) should appear at the given row and column position.
        */
         private String getElementSymbol(int row, int col) {
             
                  // This 2D array represents the layout of the periodic table
                // Each row is an array of element symbols, with null values for empty spaces
                // This creates the characteristic shape of the periodic table
                 String[][] periodicTable = {
                           // Row 1 (Period 1): Only H and He with empty spaces between them
                          {"H", null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "He"},
                     
                          // Row 2 (Period 2): Li and Be on left, then B through Ne on right
                         {"Li", "Be", null, null, null, null, null, null, null, null, null, null, "B", "C", "N", "O", "F", "Ne"},
                         
                         // Row 3 (Period 3): Na and Mg on left, then Al through Ar on right
                        {"Na", "Mg", null, null, null, null, null, null, null, null, null, null, "Al", "Si", "P", "S", "Cl", "Ar"},

                        // Row 4 (Period 4): Complete row with K to Kr (including transition metals)
                        {"K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr"},

                         // Row 5 (Period 5): Complete row with Rb to Xe (including transition metals)
                         {"Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe"},
           
                         // Row 6 (Period 6): Cs, Ba, La then Hf to Rn (Lanthanides are shown separately)
                        {"Cs", "Ba", "La", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn"},

                         // Row 7 (Period 7): Fr, Ra, Ac then Rf to Og (Actinides are shown separately)
                        {"Fr", "Ra", "Ac", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Nh", "Fl", "Mc", "Lv", "Ts", "Og"},
                        
                        // Row 8 (Lanthanides): Elements Ce to Lu
                        {null, null, null, "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", null},

                        // Row 9 (Actinides): Elements Th to Lr
                        {null, null, null, "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr", null}

                 };
                 
                 // Check if the provided row and column indices are within the valid range
                // This prevents ArrayIndexOutOfBoundsException when accessing the array
                if (row >= 0 && row < periodicTable.length && col >= 0 && col < periodicTable[row].length) {
                        // Return the symbol at the specified position (which may be null)
                        return periodicTable[row][col];
                  }
                
                 // Return null if the coordinates are out of bounds
                return null;
             
         }
      
      
      
        
         /**
        * Adds a chemical element to the elements HashMap using its symbol as the key.
        * This method will be used to populate the periodic table with element data.
        */
         private void addElement(int atomicNumber, String symbol, String name,
                          double atomicMass, String category, String description) {
                // Create a new Element object with all the provided parameters
                // Then add it to the elements HashMap with the symbol as the key
                // This allows for quickly retrieving element data using its symbol
                elements.put(symbol, new Element(atomicNumber, symbol, name,
                        atomicMass, category, description));
          }

        
        
        
     /**
     * Class representing a chemical element in the periodic table.
     * This class stores all relevant information about a single element.
     */
        private static class Element {
        
             // The element's atomic number (number of protons)
             int atomicNumber;
             
             // The element's chemical symbol (1-3 letters, first letter capitalized)
            String symbol;
            
             // The element's full name
            String name;

            // The element's atomic mass in atomic mass units (amu)
            double atomicMass;
            
            // The element's chemical category (e.g., "Nonmetal", "Noble Gas")
            String category;

            // A brief description of the element's properties or uses
            String description;
            
            Element(int atomicNumber, String symbol, String name,
            double atomicMass, String category, String description) {
                    // Initialize all the instance variables with the provided values
                    this.atomicNumber = atomicNumber;
                    this.symbol = symbol;
                    this.name = name;
                    this.atomicMass = atomicMass;
                    this.category = category;
                    this.description = description;
            }

        }
        

         public static void main(String[] args) {
             SwingUtilities.invokeLater(() -> {
                PeriodicTable table = new PeriodicTable();
                table.setVisible(true);
            });
    }

}
  


The Final Result:

Periodic Table In Java Swing - 1

Periodic Table In Java Swing - 2

Periodic Table In Java Swing - 3

Periodic Table In Java Swing - 4

Periodic Table In Java Swing - 5



if you want the full source code click on the download button below




disclaimer: you will get the source code, and to make it work in your machine is your responsibility and to debug any error/exception is your responsibility this project is for the students who want to see an example and read the code not to get and run.